Skip to content

Commit

Permalink
#1029 Introduce search palette that can find nodes and edges through …
Browse files Browse the repository at this point in the history
…autocomplete with the capability to visually highlight found elements. (eclipse-glsp#242)

- Introduce base class for autocomplete palettes supporting flexible suggestions
- Extend AutoCompleteWidget with visibleSuggestionsChanged and selectedSuggestionChanged callbacks
- Provide reason if AutoCompleteWidget closes

Part of eclipse-glsp/glsp#1029
  • Loading branch information
aylin-sarioglu authored and holkerveen committed Dec 21, 2024
1 parent 61d1a69 commit 84ea397
Show file tree
Hide file tree
Showing 14 changed files with 510 additions and 13 deletions.
3 changes: 2 additions & 1 deletion examples/workflow-glsp/src/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
DiamondNodeView,
editLabelFeature,
GEdge,
glspAccessibilityModule,
GLSPGraph,
GLSPProjectionView,
GridSnapper,
Expand Down Expand Up @@ -84,7 +85,7 @@ export function initializeWorkflowDiagramContainer(
widgetId: string,
...containerConfiguration: ContainerConfiguration
): Container {
initializeDiagramContainer(container, workflowDiagramModule, directTaskEditor, ...containerConfiguration);
initializeDiagramContainer(container, workflowDiagramModule, directTaskEditor, glspAccessibilityModule, ...containerConfiguration);
overrideViewerOptions(container, {
baseDiv: widgetId,
hiddenDiv: widgetId + '_hidden'
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"devDependencies": {
"@eclipse-glsp/dev": "1.1.0-next.164cf99.124",
"@types/lodash": "4.14.191",
"@types/node": "16.x"
},
"engines": {
Expand Down
22 changes: 22 additions & 0 deletions packages/client/css/autocomplete-palette.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/********************************************************************************
* Copyright (c) 2023 Business Informatics Group (TU Wien) and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

.autocomplete-palette {
position: absolute;
left: 20px;
top: 20px;
width: 400px;
}
17 changes: 16 additions & 1 deletion packages/client/css/glsp-sprotty.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2022 EclipseSource and others.
* Copyright (c) 2019-2023 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -254,3 +254,18 @@
fill: transparent;
width: var(--theia-scrollbar-rail-width);
}

.search-hidden {
opacity: 0.2;
}

.search-highlighted .sprotty-node,
.search-highlighted .sprotty-edge {
stroke-width: 1.5px;
stroke-width: 2px;
stroke-dashoffset: 5;
stroke-dasharray: 5, 5;
stroke-linecap: round;
stroke: rgb(218, 89, 15) !important;
stroke: #130bf7 !important;
}
2 changes: 2 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@
"dependencies": {
"@eclipse-glsp/protocol": "1.1.0-next",
"autocompleter": "5.1.0",
"lodash": "4.17.21",
"sprotty": "0.13.0-next.f4445dd.342"
},
"devDependencies": {
"@types/lodash": "4.14.191",
"@vscode/codicons": "^0.0.25"
},
"publishConfig": {
Expand Down
38 changes: 31 additions & 7 deletions packages/client/src/base/auto-complete/auto-complete-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface AutoCompleteSettings {
readonly showOnFocus?: boolean;
}

export type CloseReason = 'submission' | 'blur' | 'escape';
export interface InputValidator {
validate(input: string): Promise<ValidationStatus>;
}
Expand All @@ -48,6 +49,11 @@ export interface TextSubmitHandler {
executeFromTextOnlyInput(input: string): void;
}

export interface AutoCompleteWidgetOptions {
visibleSuggestionsChanged?: (suggestions: LabeledAction[]) => void;
selectedSuggestionChanged?: (suggestion?: LabeledAction) => void;
}

// eslint-disable-next-line @typescript-eslint/no-var-requires
const configureAutocomplete: (settings: AutocompleteSettings<LabeledAction>) => AutocompleteResult = require('autocompleter');

Expand Down Expand Up @@ -78,8 +84,9 @@ export class AutoCompleteWidget {
public suggestionProvider: SuggestionProvider,
public suggestionSubmitHandler: SuggestionSubmitHandler,
// eslint-disable-next-line @typescript-eslint/no-empty-function
protected notifyClose: () => void = () => {},
protected logger?: ILogger
protected notifyClose: (reason: CloseReason) => void = () => {},
protected logger?: ILogger,
protected options?: AutoCompleteWidgetOptions
) {}

configureValidation(
Expand Down Expand Up @@ -109,13 +116,18 @@ export class AutoCompleteWidget {
inputElement.autocomplete = 'false';
inputElement.style.width = '100%';
inputElement.addEventListener('keydown', event => this.handleKeyDown(event));
inputElement.addEventListener('blur', () => window.setTimeout(() => this.notifyClose(), 200));
inputElement.addEventListener('blur', () => {
if (this.containerElement.style.visibility !== 'hidden') {
window.setTimeout(() => this.notifyClose('blur'), 200);
}
});

return inputElement;
}

protected handleKeyDown(event: KeyboardEvent): void {
if (matchesKeystroke(event, 'Escape')) {
this.notifyClose();
this.notifyClose('escape');
return;
}
if (matchesKeystroke(event, 'Enter') && !this.isInputElementChanged() && this.isSuggestionAvailable()) {
Expand All @@ -133,7 +145,7 @@ export class AutoCompleteWidget {
}
if (this.textSubmitHandler) {
this.executeFromTextOnlyInput();
this.notifyClose();
this.notifyClose('submission');
}
}

Expand Down Expand Up @@ -175,6 +187,17 @@ export class AutoCompleteWidget {
// move container into our UIExtension container as this is already positioned correctly
if (this.containerElement) {
this.containerElement.appendChild(container);

if (this.options && this.options.selectedSuggestionChanged) {
const selectedElement = container.querySelector('.selected');
// eslint-disable-next-line no-null/no-null
if (selectedElement !== null && selectedElement !== undefined) {
const index = Array.from(container.children).indexOf(selectedElement);
this.options.selectedSuggestionChanged(this.contextActions?.[index]);
} else {
this.options.selectedSuggestionChanged(undefined);
}
}
}
}

Expand All @@ -189,6 +212,7 @@ export class AutoCompleteWidget {
.then(actions => {
this.contextActions = this.filterActions(text, actions);
update(this.contextActions);
this.options?.visibleSuggestionsChanged?.(this.contextActions);
this.onLoaded('success');
})
.catch(reason => {
Expand Down Expand Up @@ -228,7 +252,7 @@ export class AutoCompleteWidget {
if (item.icon) {
this.renderIcon(itemElement, item.icon);
}
itemElement.innerHTML += item.label.replace(regex, match => '<em>' + match + '</em>');
itemElement.innerHTML += item.label.replace(regex, match => '<em>' + match + '</em>').replace(/ /g, '&nbsp;');
return itemElement;
}

Expand Down Expand Up @@ -257,7 +281,7 @@ export class AutoCompleteWidget {
window.setTimeout(() => this.inputElement.dispatchEvent(new Event('keyup')));
} else {
this.executeFromSuggestion(item);
this.notifyClose();
this.notifyClose('submission');
}
}

Expand Down
26 changes: 26 additions & 0 deletions packages/client/src/features/accessibility/di.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/********************************************************************************
* Copyright (c) 2023 Business Informatics Group (TU Wien) and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ContainerModule } from 'inversify';
import { configureSearchPaletteModule } from './search/di.config';

/**
* Enables the accessibility tools for a keyboard-only-usage
*/
export const glspAccessibilityModule = new ContainerModule((bind, _unbind, isBound, rebind) => {
const context = { bind, isBound, rebind };
configureSearchPaletteModule(context);
});
31 changes: 31 additions & 0 deletions packages/client/src/features/accessibility/search/di.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/********************************************************************************
* Copyright (c) 2023 Business Informatics Group (TU Wien) and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { bindAsService, BindingContext } from '@eclipse-glsp/protocol';
import { ContainerModule } from 'inversify';
import { TYPES } from '../../../base/types';
import { SearchAutocompletePalette } from './search-palette';
import { SearchAutocompletePaletteTool } from './search-tool';

export const glspSearchPaletteModule = new ContainerModule((bind, _unbind, isBound, rebind) => {
const context = { bind, isBound, rebind };
configureSearchPaletteModule(context);
});

export function configureSearchPaletteModule(context: Pick<BindingContext, 'bind'>): void {
bindAsService(context, TYPES.IUIExtension, SearchAutocompletePalette);
bindAsService(context, TYPES.IDefaultTool, SearchAutocompletePaletteTool);
}
Loading

0 comments on commit 84ea397

Please sign in to comment.