mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-12 11:49:27 +00:00
Use eslint&prettier for code linting&formatting
This commit is contained in:
committed by
Francesco Stasi
parent
2a3873a923
commit
0592199858
@@ -5,7 +5,6 @@ import { Props } from 'react-select/src/components';
|
||||
import { ThemeConfig } from 'react-select/src/theme';
|
||||
|
||||
export class ArduinoSelect<T> extends Select<T> {
|
||||
|
||||
constructor(props: Readonly<Props<T, false>>) {
|
||||
super(props);
|
||||
}
|
||||
@@ -13,51 +12,55 @@ export class ArduinoSelect<T> extends Select<T> {
|
||||
render(): React.ReactNode {
|
||||
const controlHeight = 27; // from `monitor.css` -> `.serial-monitor-container .head` (`height: 27px;`)
|
||||
const styles: Styles<T, false> = {
|
||||
control: styles => ({
|
||||
control: (styles) => ({
|
||||
...styles,
|
||||
minWidth: 120,
|
||||
color: 'var(--theia-foreground)'
|
||||
color: 'var(--theia-foreground)',
|
||||
}),
|
||||
dropdownIndicator: styles => ({
|
||||
dropdownIndicator: (styles) => ({
|
||||
...styles,
|
||||
padding: 0
|
||||
padding: 0,
|
||||
}),
|
||||
indicatorSeparator: () => ({
|
||||
display: 'none'
|
||||
display: 'none',
|
||||
}),
|
||||
indicatorsContainer: () => ({
|
||||
padding: '0px 5px'
|
||||
padding: '0px 5px',
|
||||
}),
|
||||
menu: styles => ({
|
||||
menu: (styles) => ({
|
||||
...styles,
|
||||
marginTop: 0
|
||||
})
|
||||
marginTop: 0,
|
||||
}),
|
||||
};
|
||||
const theme: ThemeConfig = theme => ({
|
||||
const theme: ThemeConfig = (theme) => ({
|
||||
...theme,
|
||||
borderRadius: 0,
|
||||
spacing: {
|
||||
controlHeight,
|
||||
baseUnit: 2,
|
||||
menuGutter: 4
|
||||
}, colors: {
|
||||
menuGutter: 4,
|
||||
},
|
||||
colors: {
|
||||
...theme.colors,
|
||||
// `primary50`??? it's crazy but apparently, without this, we would get a light-blueish
|
||||
// color when selecting an option in the select by clicking and then not releasing the button.
|
||||
// https://react-select.com/styles#overriding-the-theme
|
||||
primary50: 'var(--theia-list-activeSelectionBackground)',
|
||||
}
|
||||
},
|
||||
});
|
||||
const DropdownIndicator = () => <span className='fa fa-caret-down caret' />;
|
||||
return <Select
|
||||
{...this.props}
|
||||
className='theia-select'
|
||||
components={{ DropdownIndicator }}
|
||||
theme={theme}
|
||||
styles={styles}
|
||||
classNamePrefix='arduino-select'
|
||||
isSearchable={false}
|
||||
/>
|
||||
const DropdownIndicator = () => (
|
||||
<span className="fa fa-caret-down caret" />
|
||||
);
|
||||
return (
|
||||
<Select
|
||||
{...this.props}
|
||||
className="theia-select"
|
||||
components={{ DropdownIndicator }}
|
||||
theme={theme}
|
||||
styles={styles}
|
||||
classNamePrefix="arduino-select"
|
||||
isSearchable={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,29 +3,34 @@ import { Installable } from '../../../common/protocol/installable';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
|
||||
export class ComponentListItem<T extends ArduinoComponent> extends React.Component<ComponentListItem.Props<T>, ComponentListItem.State> {
|
||||
|
||||
export class ComponentListItem<
|
||||
T extends ArduinoComponent
|
||||
> extends React.Component<ComponentListItem.Props<T>, ComponentListItem.State> {
|
||||
constructor(props: ComponentListItem.Props<T>) {
|
||||
super(props);
|
||||
if (props.item.installable) {
|
||||
const version = props.item.availableVersions.filter(version => version !== props.item.installedVersion)[0];
|
||||
const version = props.item.availableVersions.filter(
|
||||
(version) => version !== props.item.installedVersion
|
||||
)[0];
|
||||
this.state = {
|
||||
selectedVersion: version
|
||||
selectedVersion: version,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected async install(item: T): Promise<void> {
|
||||
const toInstall = this.state.selectedVersion;
|
||||
const version = this.props.item.availableVersions.filter(version => version !== this.state.selectedVersion)[0];
|
||||
const version = this.props.item.availableVersions.filter(
|
||||
(version) => version !== this.state.selectedVersion
|
||||
)[0];
|
||||
this.setState({
|
||||
selectedVersion: version
|
||||
selectedVersion: version,
|
||||
});
|
||||
try {
|
||||
await this.props.install(item, toInstall);
|
||||
} catch {
|
||||
this.setState({
|
||||
selectedVersion: toInstall
|
||||
selectedVersion: toInstall,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -47,14 +52,15 @@ export class ComponentListItem<T extends ArduinoComponent> extends React.Compone
|
||||
this.onVersionChange.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ComponentListItem {
|
||||
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
readonly item: T;
|
||||
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
|
||||
readonly install: (
|
||||
item: T,
|
||||
version?: Installable.Version
|
||||
) => Promise<void>;
|
||||
readonly uninstall: (item: T) => Promise<void>;
|
||||
readonly itemRenderer: ListItemRenderer<T>;
|
||||
}
|
||||
@@ -62,5 +68,4 @@ export namespace ComponentListItem {
|
||||
export interface State {
|
||||
selectedVersion?: Installable.Version;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@ import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ComponentListItem } from './component-list-item';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
|
||||
export class ComponentList<T extends ArduinoComponent> extends React.Component<ComponentList.Props<T>> {
|
||||
|
||||
export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
||||
ComponentList.Props<T>
|
||||
> {
|
||||
protected container?: HTMLElement;
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <div
|
||||
className={'items-container'}
|
||||
ref={this.setRef}>
|
||||
{this.props.items.map(item => this.createItem(item))}
|
||||
</div>;
|
||||
return (
|
||||
<div className={'items-container'} ref={this.setRef}>
|
||||
{this.props.items.map((item) => this.createItem(item))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
@@ -24,29 +25,32 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<C
|
||||
|
||||
protected setRef = (element: HTMLElement | null) => {
|
||||
this.container = element || undefined;
|
||||
}
|
||||
};
|
||||
|
||||
protected createItem(item: T): React.ReactNode {
|
||||
return <ComponentListItem<T>
|
||||
key={this.props.itemLabel(item)}
|
||||
item={item}
|
||||
itemRenderer={this.props.itemRenderer}
|
||||
install={this.props.install}
|
||||
uninstall={this.props.uninstall} />
|
||||
return (
|
||||
<ComponentListItem<T>
|
||||
key={this.props.itemLabel(item)}
|
||||
item={item}
|
||||
itemRenderer={this.props.itemRenderer}
|
||||
install={this.props.install}
|
||||
uninstall={this.props.uninstall}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ComponentList {
|
||||
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
readonly items: T[];
|
||||
readonly itemLabel: (item: T) => string;
|
||||
readonly itemDeprecated: (item: T) => boolean;
|
||||
readonly itemRenderer: ListItemRenderer<T>;
|
||||
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
|
||||
readonly install: (
|
||||
item: T,
|
||||
version?: Installable.Version
|
||||
) => Promise<void>;
|
||||
readonly uninstall: (item: T) => Promise<void>;
|
||||
readonly resolveContainer: (element: HTMLElement) => void;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,20 +13,26 @@ import { ComponentList } from './component-list';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
import { ResponseServiceImpl } from '../../response-service-impl';
|
||||
|
||||
export class FilterableListContainer<T extends ArduinoComponent> extends React.Component<FilterableListContainer.Props<T>, FilterableListContainer.State<T>> {
|
||||
|
||||
export class FilterableListContainer<
|
||||
T extends ArduinoComponent
|
||||
> extends React.Component<
|
||||
FilterableListContainer.Props<T>,
|
||||
FilterableListContainer.State<T>
|
||||
> {
|
||||
constructor(props: Readonly<FilterableListContainer.Props<T>>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filterText: '',
|
||||
items: []
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.search = debounce(this.search, 500);
|
||||
this.handleFilterTextChange('');
|
||||
this.props.filterTextChangeEvent(this.handleFilterTextChange.bind(this));
|
||||
this.props.filterTextChangeEvent(
|
||||
this.handleFilterTextChange.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
@@ -36,11 +42,13 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <div className={'filterable-list-container'}>
|
||||
{this.renderSearchFilter()}
|
||||
{this.renderSearchBar()}
|
||||
{this.renderComponentList()}
|
||||
</div>
|
||||
return (
|
||||
<div className={'filterable-list-container'}>
|
||||
{this.renderSearchFilter()}
|
||||
{this.renderSearchBar()}
|
||||
{this.renderComponentList()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected renderSearchFilter(): React.ReactNode {
|
||||
@@ -48,56 +56,67 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
|
||||
}
|
||||
|
||||
protected renderSearchBar(): React.ReactNode {
|
||||
return <SearchBar
|
||||
resolveFocus={this.props.resolveFocus}
|
||||
filterText={this.state.filterText}
|
||||
onFilterTextChanged={this.handleFilterTextChange}
|
||||
/>
|
||||
return (
|
||||
<SearchBar
|
||||
resolveFocus={this.props.resolveFocus}
|
||||
filterText={this.state.filterText}
|
||||
onFilterTextChanged={this.handleFilterTextChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected renderComponentList(): React.ReactNode {
|
||||
const { itemLabel, itemDeprecated, resolveContainer, itemRenderer } = this.props;
|
||||
return <ComponentList<T>
|
||||
items={this.state.items}
|
||||
itemLabel={itemLabel}
|
||||
itemDeprecated={itemDeprecated}
|
||||
itemRenderer={itemRenderer}
|
||||
install={this.install.bind(this)}
|
||||
uninstall={this.uninstall.bind(this)}
|
||||
resolveContainer={resolveContainer}
|
||||
/>
|
||||
const { itemLabel, itemDeprecated, resolveContainer, itemRenderer } =
|
||||
this.props;
|
||||
return (
|
||||
<ComponentList<T>
|
||||
items={this.state.items}
|
||||
itemLabel={itemLabel}
|
||||
itemDeprecated={itemDeprecated}
|
||||
itemRenderer={itemRenderer}
|
||||
install={this.install.bind(this)}
|
||||
uninstall={this.uninstall.bind(this)}
|
||||
resolveContainer={resolveContainer}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected handleFilterTextChange = (filterText: string = this.state.filterText) => {
|
||||
protected handleFilterTextChange = (
|
||||
filterText: string = this.state.filterText
|
||||
) => {
|
||||
this.setState({ filterText });
|
||||
this.search(filterText);
|
||||
}
|
||||
};
|
||||
|
||||
protected search(query: string): void {
|
||||
const { searchable } = this.props;
|
||||
searchable.search({ query: query.trim() }).then(items => this.setState({ items: this.sort(items) }));
|
||||
searchable
|
||||
.search({ query: query.trim() })
|
||||
.then((items) => this.setState({ items: this.sort(items) }));
|
||||
}
|
||||
|
||||
protected sort(items: T[]): T[] {
|
||||
// debugger;
|
||||
const { itemLabel, itemDeprecated } = this.props;
|
||||
return items.sort((left, right) => {
|
||||
|
||||
// always put deprecated items at the bottom of the list
|
||||
if (itemDeprecated(left)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return itemLabel(left).localeCompare(itemLabel(right))
|
||||
return itemLabel(left).localeCompare(itemLabel(right));
|
||||
});
|
||||
}
|
||||
|
||||
protected async install(item: T, version: Installable.Version): Promise<void> {
|
||||
protected async install(
|
||||
item: T,
|
||||
version: Installable.Version
|
||||
): Promise<void> {
|
||||
const { install, searchable } = this.props;
|
||||
await Installable.doWithProgress({
|
||||
...this.props,
|
||||
progressText: `Processing ${item.name}:${version}`,
|
||||
run: ({ progressId }) => install({ item, progressId, version })
|
||||
run: ({ progressId }) => install({ item, progressId, version }),
|
||||
});
|
||||
const items = await searchable.search({ query: this.state.filterText });
|
||||
this.setState({ items: this.sort(items) });
|
||||
@@ -108,7 +127,7 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
|
||||
title: 'Uninstall',
|
||||
msg: `Do you want to uninstall ${item.name}?`,
|
||||
ok: 'Yes',
|
||||
cancel: 'No'
|
||||
cancel: 'No',
|
||||
}).open();
|
||||
if (!ok) {
|
||||
return;
|
||||
@@ -116,17 +135,17 @@ export class FilterableListContainer<T extends ArduinoComponent> extends React.C
|
||||
const { uninstall, searchable } = this.props;
|
||||
await Installable.doWithProgress({
|
||||
...this.props,
|
||||
progressText: `Processing ${item.name}${item.installedVersion ? `:${item.installedVersion}` : ''}`,
|
||||
run: ({ progressId }) => uninstall({ item, progressId })
|
||||
progressText: `Processing ${item.name}${
|
||||
item.installedVersion ? `:${item.installedVersion}` : ''
|
||||
}`,
|
||||
run: ({ progressId }) => uninstall({ item, progressId }),
|
||||
});
|
||||
const items = await searchable.search({ query: this.state.filterText });
|
||||
this.setState({ items: this.sort(items) });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace FilterableListContainer {
|
||||
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
readonly container: ListWidget<T>;
|
||||
readonly searchable: Searchable<T>;
|
||||
@@ -138,8 +157,22 @@ export namespace FilterableListContainer {
|
||||
readonly filterTextChangeEvent: Event<string | undefined>;
|
||||
readonly messageService: MessageService;
|
||||
readonly responseService: ResponseServiceImpl;
|
||||
readonly install: ({ item, progressId, version }: { item: T, progressId: string, version: Installable.Version }) => Promise<void>;
|
||||
readonly uninstall: ({ item, progressId }: { item: T, progressId: string }) => Promise<void>;
|
||||
readonly install: ({
|
||||
item,
|
||||
progressId,
|
||||
version,
|
||||
}: {
|
||||
item: T;
|
||||
progressId: string;
|
||||
version: Installable.Version;
|
||||
}) => Promise<void>;
|
||||
readonly uninstall: ({
|
||||
item,
|
||||
progressId,
|
||||
}: {
|
||||
item: T;
|
||||
progressId: string;
|
||||
}) => Promise<void>;
|
||||
readonly commandService: CommandService;
|
||||
}
|
||||
|
||||
@@ -147,5 +180,4 @@ export namespace FilterableListContainer {
|
||||
filterText: string;
|
||||
items: T[];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,17 +7,18 @@ import { ComponentListItem } from './component-list-item';
|
||||
|
||||
@injectable()
|
||||
export class ListItemRenderer<T extends ArduinoComponent> {
|
||||
|
||||
@inject(WindowService)
|
||||
protected windowService: WindowService;
|
||||
|
||||
protected onMoreInfoClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
||||
protected onMoreInfoClick = (
|
||||
event: React.SyntheticEvent<HTMLAnchorElement, Event>
|
||||
) => {
|
||||
const { target } = event.nativeEvent;
|
||||
if (target instanceof HTMLAnchorElement) {
|
||||
this.windowService.openNewWindow(target.href, { external: true });
|
||||
event.nativeEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderItem(
|
||||
input: ComponentListItem.State & { item: T },
|
||||
@@ -25,78 +26,98 @@ export class ListItemRenderer<T extends ArduinoComponent> {
|
||||
uninstall: (item: T) => Promise<void>,
|
||||
onVersionChange: (version: Installable.Version) => void
|
||||
): React.ReactNode {
|
||||
|
||||
const { item } = input;
|
||||
let nameAndAuthor: JSX.Element;
|
||||
if (item.name && item.author) {
|
||||
const name = <span className='name'>{item.name}</span>;
|
||||
const author = <span className='author'>{item.author}</span>;
|
||||
nameAndAuthor = <span>{name} by {author}</span>
|
||||
const name = <span className="name">{item.name}</span>;
|
||||
const author = <span className="author">{item.author}</span>;
|
||||
nameAndAuthor = (
|
||||
<span>
|
||||
{name} by {author}
|
||||
</span>
|
||||
);
|
||||
} else if (item.name) {
|
||||
nameAndAuthor = <span className='name'>{item.name}</span>;
|
||||
nameAndAuthor = <span className="name">{item.name}</span>;
|
||||
} else if ((item as any).id) {
|
||||
nameAndAuthor = <span className='name'>{(item as any).id}</span>;
|
||||
nameAndAuthor = <span className="name">{(item as any).id}</span>;
|
||||
} else {
|
||||
nameAndAuthor = <span className='name'>Unknown</span>;
|
||||
nameAndAuthor = <span className="name">Unknown</span>;
|
||||
}
|
||||
const onClickUninstall = () => uninstall(item);
|
||||
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
||||
<span className='version'>Version {item.installedVersion}</span>
|
||||
<span className='installed' onClick={onClickUninstall} />
|
||||
</div>;
|
||||
const installedVersion = !!item.installedVersion && (
|
||||
<div className="version-info">
|
||||
<span className="version">Version {item.installedVersion}</span>
|
||||
<span className="installed" onClick={onClickUninstall} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const summary = <div className='summary'>{item.summary}</div>;
|
||||
const description = <div className='summary'>{item.description}</div>;
|
||||
const summary = <div className="summary">{item.summary}</div>;
|
||||
const description = <div className="summary">{item.description}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
|
||||
const moreInfo = !!item.moreInfoLink && (
|
||||
<a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>
|
||||
More info
|
||||
</a>
|
||||
);
|
||||
const onClickInstall = () => install(item);
|
||||
const installButton = item.installable &&
|
||||
<button className='theia-button install' onClick={onClickInstall}>INSTALL</button>;
|
||||
const installButton = item.installable && (
|
||||
<button className="theia-button install" onClick={onClickInstall}>
|
||||
INSTALL
|
||||
</button>
|
||||
);
|
||||
|
||||
const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const onSelectChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
const version = event.target.value;
|
||||
if (version) {
|
||||
onVersionChange(version);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const versions = (() => {
|
||||
const { availableVersions } = item;
|
||||
if (availableVersions.length === 0) {
|
||||
return undefined;
|
||||
} else if (availableVersions.length === 1) {
|
||||
return <label>{availableVersions[0]}</label>
|
||||
return <label>{availableVersions[0]}</label>;
|
||||
} else {
|
||||
return <select
|
||||
className='theia-select'
|
||||
value={input.selectedVersion}
|
||||
onChange={onSelectChange}>
|
||||
{
|
||||
item.availableVersions
|
||||
.filter(version => version !== item.installedVersion) // Filter the version that is currently installed.
|
||||
.map(version => <option value={version} key={version}>{version}</option>)
|
||||
}
|
||||
</select>;
|
||||
return (
|
||||
<select
|
||||
className="theia-select"
|
||||
value={input.selectedVersion}
|
||||
onChange={onSelectChange}
|
||||
>
|
||||
{item.availableVersions
|
||||
.filter(
|
||||
(version) => version !== item.installedVersion
|
||||
) // Filter the version that is currently installed.
|
||||
.map((version) => (
|
||||
<option value={version} key={version}>
|
||||
{version}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
return <div className='component-list-item noselect'>
|
||||
<div className='header'>
|
||||
{nameAndAuthor}
|
||||
{installedVersion}
|
||||
return (
|
||||
<div className="component-list-item noselect">
|
||||
<div className="header">
|
||||
{nameAndAuthor}
|
||||
{installedVersion}
|
||||
</div>
|
||||
<div className="content">
|
||||
{summary}
|
||||
{description}
|
||||
</div>
|
||||
<div className="info">{moreInfo}</div>
|
||||
<div className="footer">
|
||||
{installButton}
|
||||
{versions}
|
||||
</div>
|
||||
</div>
|
||||
<div className='content'>
|
||||
{summary}
|
||||
{description}
|
||||
</div>
|
||||
<div className='info'>
|
||||
{moreInfo}
|
||||
</div>
|
||||
<div className='footer'>
|
||||
{installButton}
|
||||
{versions}
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ListWidget } from './list-widget';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListWidgetFrontendContribution<T extends ArduinoComponent> extends AbstractViewContribution<ListWidget<T>> implements FrontendApplicationContribution {
|
||||
|
||||
async initializeLayout(): Promise<void> {
|
||||
}
|
||||
export abstract class ListWidgetFrontendContribution<T extends ArduinoComponent>
|
||||
extends AbstractViewContribution<ListWidget<T>>
|
||||
implements FrontendApplicationContribution
|
||||
{
|
||||
async initializeLayout(): Promise<void> {}
|
||||
|
||||
registerMenus(): void {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,15 +8,20 @@ import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { CommandService } from '@theia/core/lib/common/command';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { Installable, Searchable, ArduinoComponent } from '../../../common/protocol';
|
||||
import {
|
||||
Installable,
|
||||
Searchable,
|
||||
ArduinoComponent,
|
||||
} from '../../../common/protocol';
|
||||
import { FilterableListContainer } from './filterable-list-container';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
import { NotificationCenter } from '../../notification-center';
|
||||
import { ResponseServiceImpl } from '../../response-service-impl';
|
||||
|
||||
@injectable()
|
||||
export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget {
|
||||
|
||||
export abstract class ListWidget<
|
||||
T extends ArduinoComponent
|
||||
> extends ReactWidget {
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
|
||||
@@ -34,7 +39,9 @@ export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget
|
||||
*/
|
||||
protected focusNode: HTMLElement | undefined;
|
||||
protected readonly deferredContainer = new Deferred<HTMLElement>();
|
||||
protected readonly filterTextChangeEmitter = new Emitter<string | undefined>();
|
||||
protected readonly filterTextChangeEmitter = new Emitter<
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
constructor(protected options: ListWidget.Options<T>) {
|
||||
super();
|
||||
@@ -42,13 +49,13 @@ export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget
|
||||
this.id = id;
|
||||
this.title.label = label;
|
||||
this.title.caption = label;
|
||||
this.title.iconClass = iconClass
|
||||
this.title.iconClass = iconClass;
|
||||
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
|
||||
}
|
||||
suppressScrollX: true,
|
||||
};
|
||||
this.toDispose.push(this.filterTextChangeEmitter);
|
||||
}
|
||||
|
||||
@@ -56,9 +63,15 @@ export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget
|
||||
protected init(): void {
|
||||
this.update();
|
||||
this.toDispose.pushAll([
|
||||
this.notificationCenter.onIndexUpdated(() => this.refresh(undefined)),
|
||||
this.notificationCenter.onDaemonStarted(() => this.refresh(undefined)),
|
||||
this.notificationCenter.onDaemonStopped(() => this.refresh(undefined))
|
||||
this.notificationCenter.onIndexUpdated(() =>
|
||||
this.refresh(undefined)
|
||||
),
|
||||
this.notificationCenter.onDaemonStarted(() =>
|
||||
this.refresh(undefined)
|
||||
),
|
||||
this.notificationCenter.onDaemonStopped(() =>
|
||||
this.refresh(undefined)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -85,29 +98,46 @@ export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget
|
||||
this.focusNode = element;
|
||||
};
|
||||
|
||||
protected async install({ item, progressId, version }: { item: T, progressId: string, version: Installable.Version }): Promise<void> {
|
||||
protected async install({
|
||||
item,
|
||||
progressId,
|
||||
version,
|
||||
}: {
|
||||
item: T;
|
||||
progressId: string;
|
||||
version: Installable.Version;
|
||||
}): Promise<void> {
|
||||
return this.options.installable.install({ item, progressId, version });
|
||||
}
|
||||
|
||||
protected async uninstall({ item, progressId }: { item: T, progressId: string }): Promise<void> {
|
||||
protected async uninstall({
|
||||
item,
|
||||
progressId,
|
||||
}: {
|
||||
item: T;
|
||||
progressId: string;
|
||||
}): Promise<void> {
|
||||
return this.options.installable.uninstall({ item, progressId });
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <FilterableListContainer<T>
|
||||
container={this}
|
||||
resolveContainer={this.deferredContainer.resolve}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
searchable={this.options.searchable}
|
||||
install={this.install.bind(this)}
|
||||
uninstall={this.uninstall.bind(this)}
|
||||
itemLabel={this.options.itemLabel}
|
||||
itemDeprecated={this.options.itemDeprecated}
|
||||
itemRenderer={this.options.itemRenderer}
|
||||
filterTextChangeEvent={this.filterTextChangeEmitter.event}
|
||||
messageService={this.messageService}
|
||||
commandService={this.commandService}
|
||||
responseService={this.responseService} />;
|
||||
return (
|
||||
<FilterableListContainer<T>
|
||||
container={this}
|
||||
resolveContainer={this.deferredContainer.resolve}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
searchable={this.options.searchable}
|
||||
install={this.install.bind(this)}
|
||||
uninstall={this.uninstall.bind(this)}
|
||||
itemLabel={this.options.itemLabel}
|
||||
itemDeprecated={this.options.itemDeprecated}
|
||||
itemRenderer={this.options.itemRenderer}
|
||||
filterTextChangeEvent={this.filterTextChangeEmitter.event}
|
||||
messageService={this.messageService}
|
||||
commandService={this.commandService}
|
||||
responseService={this.responseService}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +145,9 @@ export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget
|
||||
* If it is `undefined`, updates the view state by re-running the search with the current `filterText` term.
|
||||
*/
|
||||
refresh(filterText: string | undefined): void {
|
||||
this.deferredContainer.promise.then(() => this.filterTextChangeEmitter.fire(filterText));
|
||||
this.deferredContainer.promise.then(() =>
|
||||
this.filterTextChangeEmitter.fire(filterText)
|
||||
);
|
||||
}
|
||||
|
||||
updateScrollBar(): void {
|
||||
@@ -123,7 +155,6 @@ export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget
|
||||
this.scrollBar.update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ListWidget {
|
||||
|
||||
@@ -1,38 +1,39 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export class SearchBar extends React.Component<SearchBar.Props> {
|
||||
|
||||
constructor(props: Readonly<SearchBar.Props>) {
|
||||
super(props);
|
||||
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <input
|
||||
return (
|
||||
<input
|
||||
ref={this.setRef}
|
||||
className={`theia-input ${SearchBar.Styles.SEARCH_BAR_CLASS}`}
|
||||
type='text'
|
||||
placeholder='Filter your search...'
|
||||
type="text"
|
||||
placeholder="Filter your search..."
|
||||
size={1}
|
||||
value={this.props.filterText}
|
||||
onChange={this.handleFilterTextChange}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private setRef = (element: HTMLElement | null) => {
|
||||
if (this.props.resolveFocus) {
|
||||
this.props.resolveFocus(element || undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleFilterTextChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
private handleFilterTextChange(
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
): void {
|
||||
this.props.onFilterTextChanged(event.target.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace SearchBar {
|
||||
|
||||
export interface Props {
|
||||
filterText: string;
|
||||
onFilterTextChanged(filterText: string): void;
|
||||
@@ -42,5 +43,4 @@ export namespace SearchBar {
|
||||
export namespace Styles {
|
||||
export const SEARCH_BAR_CLASS = 'search-bar';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user