Skip to content

Commit

Permalink
File dialog enhancements
Browse files Browse the repository at this point in the history
- Add text input to locationList Renderer
- Add 'navigate upward' icon
- Fix icon focus behavior when disabled

Signed-off-by: Kenneth Marut <kenneth.marut@ericsson.com>
  • Loading branch information
kenneth-marut-work committed Feb 10, 2021
1 parent f1d4690 commit d47c8ff
Show file tree
Hide file tree
Showing 7 changed files with 393 additions and 31 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Change Log

## v1.11.0

- [filesystem] add text input and navigate up icon to file dialog [#8748](https://github.com/eclipse-theia/theia/pull/8748)

<a name="breaking_changes_1.11.0">[Breaking Changes:](#breaking_changes_1.11.0)</a>

- [filesystem] `FileDialog` and `LocationListRenderer` now require `FileService` to be passed into constructor for text-based file dialog navigation in browser [#8748](https://github.com/eclipse-theia/theia/pull/8748)

## v1.10.0 - 1/28/2021

- [api-samples] added example on how to contribute toggleable toolbar items [#8968](https://github.com/eclipse-theia/theia/pull/8968)
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/browser/style/tabs.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
--theia-private-horizontal-tab-scrollbar-rail-height: 7px;
--theia-private-horizontal-tab-scrollbar-height: 5px;
--theia-tabbar-toolbar-z-index: 1001;
--theia-toolbar-active-transform-scale: 1.272019649;
}

/*-----------------------------------------------------------------------------
Expand Down Expand Up @@ -352,7 +353,7 @@ body.theia-editor-highlightModifiedTabs
}

.p-TabBar-toolbar .item.enabled.active {
transform: scale(1.272019649);
transform: scale(var(--theia-toolbar-active-transform-scale));
}

.p-TabBar-toolbar .item.toggled {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,9 @@ export class FileDialogModel extends FileTreeModel {
private isFileStatNodeSelectable(node: FileStatNode): boolean {
return !(!node.fileStat.isDirectory && this._disableFileSelection);
}

canNavigateUpward(): boolean {
const treeRoot = this.tree.root;
return FileStatNode.is(treeRoot) && !treeRoot.uri.path.isRoot;
}
}
75 changes: 65 additions & 10 deletions packages/filesystem/src/browser/file-dialog/file-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { FileDialogWidget } from './file-dialog-widget';
import { FileDialogTreeFiltersRenderer, FileDialogTreeFilters } from './file-dialog-tree-filters-renderer';
import URI from '@theia/core/lib/common/uri';
import { Panel } from '@phosphor/widgets';
import { FileService } from '../file-service';

export const OpenFileDialogFactory = Symbol('OpenFileDialogFactory');
export interface OpenFileDialogFactory {
Expand All @@ -43,6 +44,7 @@ export const NAVIGATION_PANEL_CLASS = 'theia-NavigationPanel';
export const NAVIGATION_BACK_CLASS = 'theia-NavigationBack';
export const NAVIGATION_FORWARD_CLASS = 'theia-NavigationForward';
export const NAVIGATION_HOME_CLASS = 'theia-NavigationHome';
export const NAVIGATION_UP_CLASS = 'theia-NavigationUp';
export const NAVIGATION_LOCATION_LIST_PANEL_CLASS = 'theia-LocationListPanel';

export const FILTERS_PANEL_CLASS = 'theia-FiltersPanel';
Expand All @@ -54,6 +56,7 @@ export const FILENAME_LABEL_CLASS = 'theia-FileNameLabel';
export const FILENAME_TEXTFIELD_CLASS = 'theia-FileNameTextField';

export const CONTROL_PANEL_CLASS = 'theia-ControlPanel';
export const TOOLBAR_ITEM_TRANSFORM_TIMEOUT = 100;

export class FileDialogProps extends DialogProps {

Expand Down Expand Up @@ -116,13 +119,15 @@ export abstract class FileDialog<T> extends AbstractDialog<T> {
protected readonly back: HTMLSpanElement;
protected readonly forward: HTMLSpanElement;
protected readonly home: HTMLSpanElement;
protected readonly up: HTMLSpanElement;
protected readonly locationListRenderer: LocationListRenderer;
protected readonly treeFiltersRenderer: FileDialogTreeFiltersRenderer | undefined;
protected readonly treePanel: Panel;

constructor(
@inject(FileDialogProps) readonly props: FileDialogProps,
@inject(FileDialogWidget) readonly widget: FileDialogWidget
@inject(FileDialogWidget) readonly widget: FileDialogWidget,
@inject(FileService) readonly fileService: FileService
) {
super(props);
this.treePanel = new Panel();
Expand All @@ -145,8 +150,13 @@ export abstract class FileDialog<T> extends AbstractDialog<T> {
navigationPanel.appendChild(this.home = createIconButton('fa', 'fa-home'));
this.home.classList.add(NAVIGATION_HOME_CLASS);
this.home.title = 'Go To Initial Location';
navigationPanel.appendChild(this.up = createIconButton('fa', 'fa-level-up'));
this.up.classList.add(NAVIGATION_UP_CLASS);
this.up.title = 'Navigate Up One Directory';

this.locationListRenderer = this.createLocationListRenderer();
const locationListRendererHost = document.createElement('div');
this.locationListRenderer = this.createLocationListRenderer(locationListRendererHost);
this.toDispose.push(this.locationListRenderer);
this.locationListRenderer.host.classList.add(NAVIGATION_LOCATION_LIST_PANEL_CLASS);
navigationPanel.appendChild(this.locationListRenderer.host);

Expand All @@ -157,8 +167,8 @@ export abstract class FileDialog<T> extends AbstractDialog<T> {
return this.widget.model;
}

protected createLocationListRenderer(): LocationListRenderer {
return new LocationListRenderer(this.model);
protected createLocationListRenderer(host?: HTMLElement): LocationListRenderer {
return new LocationListRenderer(this.model, this.fileService, host);
}

protected createFileTreeFiltersRenderer(): FileDialogTreeFiltersRenderer | undefined {
Expand All @@ -176,6 +186,7 @@ export abstract class FileDialog<T> extends AbstractDialog<T> {
setEnabled(this.home, !!this.model.initialLocation
&& !!this.model.location
&& this.model.initialLocation.toString() !== this.model.location.toString());
setEnabled(this.up, this.model.canNavigateUpward());
this.locationListRenderer.render();

if (this.treeFiltersRenderer) {
Expand All @@ -185,6 +196,28 @@ export abstract class FileDialog<T> extends AbstractDialog<T> {
this.widget.update();
}

protected handleEnter(event: KeyboardEvent): boolean | void {
if (event.target instanceof HTMLTextAreaElement || this.targetIsDirectoryInput(event.target) || this.targetIsInputToggle(event.target)) {
return false;
}
this.accept();
}

protected handleEscape(event: KeyboardEvent): boolean | void {
if (event.target instanceof HTMLTextAreaElement || this.targetIsDirectoryInput(event.target)) {
return false;
}
this.close();
}

protected targetIsDirectoryInput(target: EventTarget | null): boolean {
return target instanceof HTMLInputElement && target.classList.contains(LocationListRenderer.Styles.LOCATION_TEXT_INPUT_CLASS);
}

protected targetIsInputToggle(target: EventTarget | null): boolean {
return target instanceof HTMLSpanElement && target.classList.contains(LocationListRenderer.Styles.LOCATION_INPUT_TOGGLE_CLASS);
}

protected appendFiltersPanel(): void {
if (this.treeFiltersRenderer) {
const filtersPanel = document.createElement('div');
Expand Down Expand Up @@ -216,16 +249,36 @@ export abstract class FileDialog<T> extends AbstractDialog<T> {
this.appendCloseButton('Cancel');
this.appendAcceptButton(this.getAcceptButtonLabel());

this.addKeyListener(this.back, Key.ENTER, () => this.model.navigateBackward(), 'click');
this.addKeyListener(this.forward, Key.ENTER, () => this.model.navigateForward(), 'click');
this.addKeyListener(this.back, Key.ENTER, () => {
this.addTransformEffectToIcon(this.back);
this.model.navigateBackward();
}, 'click');

this.addKeyListener(this.forward, Key.ENTER, () => {
this.addTransformEffectToIcon(this.forward);
this.model.navigateForward();
}, 'click');
this.addKeyListener(this.home, Key.ENTER, () => {
this.addTransformEffectToIcon(this.home);
if (this.model.initialLocation) {
this.model.location = this.model.initialLocation;
}
}, 'click');
this.addKeyListener(this.up, Key.ENTER, () => {
this.addTransformEffectToIcon(this.up);
if (this.model.location) {
this.model.location = this.model.location.parent;
}
}, 'click');
super.onAfterAttach(msg);
}

protected addTransformEffectToIcon(element: HTMLSpanElement): void {
const icon = element.getElementsByTagName('i')[0];
icon.classList.add('active');
setTimeout(() => icon.classList.remove('active'), TOOLBAR_ITEM_TRANSFORM_TIMEOUT);
}

protected abstract getAcceptButtonLabel(): string;

protected onActivateRequest(msg: Message): void {
Expand All @@ -239,9 +292,10 @@ export class OpenFileDialog extends FileDialog<MaybeArray<FileStatNode>> {

constructor(
@inject(OpenFileDialogProps) readonly props: OpenFileDialogProps,
@inject(FileDialogWidget) readonly widget: FileDialogWidget
@inject(FileDialogWidget) readonly widget: FileDialogWidget,
@inject(FileService) readonly fileService: FileService
) {
super(props, widget);
super(props, widget, fileService);
if (props.canSelectFiles !== undefined) {
this.widget.disableFileSelection = !props.canSelectFiles;
}
Expand Down Expand Up @@ -288,9 +342,10 @@ export class SaveFileDialog extends FileDialog<URI | undefined> {

constructor(
@inject(SaveFileDialogProps) readonly props: SaveFileDialogProps,
@inject(FileDialogWidget) readonly widget: FileDialogWidget
@inject(FileDialogWidget) readonly widget: FileDialogWidget,
@inject(FileService) readonly fileService: FileService
) {
super(props, widget);
super(props, widget, fileService);
widget.addClass(SAVE_DIALOG_CLASS);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/filesystem/src/browser/file-tree/file-tree-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export class FileTreeModel extends TreeModelImpl implements LocationService {
const node = DirNode.createRoot(fileStat);
this.navigateTo(node);
}
}).catch(() => {
// no-op, allow failures for file dialog text input
});
} else {
this.navigateTo(undefined);
Expand Down
Loading

0 comments on commit d47c8ff

Please sign in to comment.