Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for foreignObject and enhance label editing with multi-line support #171

Merged
merged 5 commits into from
Apr 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions css/edit-label.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019 EclipseSource and others.
* Copyright (c) 2019-2020 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 All @@ -14,15 +14,15 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

.label-edit input {
.label-edit input, .label-edit textarea {
background: rgba(255, 255, 255, 0.5);
border-radius: 5px;
border: 0;
width: 99%;
height: 99%;
}

.label-edit input:focus {
.label-edit input:focus, .label-edit textarea:focus {
outline: none;
outline-offset: 0px;
}
Expand Down
17 changes: 16 additions & 1 deletion examples/svg/css/diagram.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
* Copyright (c) 2017-2020 TypeFox 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 All @@ -21,3 +21,18 @@
font-size: 14pt;
text-anchor: middle;
}

.sprotty-node {
fill: darkorange;
}

.child-foreign-object div {
margin: 5px;
color: seashell;
text-align: center;
user-select: none;
}

.foreign-object {
user-select: none;
}
34 changes: 32 additions & 2 deletions examples/svg/src/di.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
* Copyright (c) 2017-2020 TypeFox 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 All @@ -17,12 +17,15 @@
import { Container, ContainerModule } from "inversify";
import {
TYPES, ConsoleLogger, LogLevel, loadDefaultModules, LocalModelSource, PreRenderedView,
SvgViewportView, ViewportRootElement, ShapedPreRenderedElement, configureModelElement
SvgViewportView, ViewportRootElement, ShapedPreRenderedElement, configureModelElement,
ForeignObjectElement, ForeignObjectView, RectangularNode, RectangularNodeView, moveFeature,
selectFeature, EditableLabel, editLabelFeature, WithEditableLabel, withEditLabelFeature, isEditableLabel
} from "../../../src";

export default () => {
require("../../../css/sprotty.css");
require("../css/diagram.css");
require("../../../css/edit-label.css");

const svgModule = new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope();
Expand All @@ -31,10 +34,37 @@ export default () => {
const context = { bind, unbind, isBound, rebind };
configureModelElement(context, 'svg', ViewportRootElement, SvgViewportView);
configureModelElement(context, 'pre-rendered', ShapedPreRenderedElement, PreRenderedView);
configureModelElement(context, 'foreign-object', ForeignObjectElement, ForeignObjectView);
configureModelElement(context, 'node', RectangleWithEditableLabel, RectangularNodeView, { enable: [withEditLabelFeature] });
configureModelElement(context, 'child-foreign-object', EditableForeignObjectElement, ForeignObjectView, {
disable: [moveFeature, selectFeature], // disable move/select as we want the parent node to react to select/move
enable: [editLabelFeature] // enable editing -- see also EditableForeignObjectElement below
});
});

const container = new Container();
loadDefaultModules(container);
container.load(svgModule);
return container;
};

export class RectangleWithEditableLabel extends RectangularNode implements WithEditableLabel {
get editableLabel() {
if (this.children.length > 0 && isEditableLabel(this.children[0])) {
return this.children[0] as EditableForeignObjectElement;
}
return undefined;
}
}

export class EditableForeignObjectElement extends ForeignObjectElement implements EditableLabel {
readonly isMultiLine = true;
get editControlDimension() { return { width: this.bounds.width, height: this.bounds.height }; }

get text(): string {
return this.code;
}
set text(newText: string) {
this.code = newText.replace('\n', '<br/>');
}
}
32 changes: 29 additions & 3 deletions examples/svg/src/standalone.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
* Copyright (c) 2017-2020 TypeFox 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 All @@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { LocalModelSource, TYPES, SModelRootSchema, ShapedPreRenderedElementSchema } from "../../../src";
import { LocalModelSource, TYPES, SModelRootSchema, ShapedPreRenderedElementSchema, ForeignObjectElementSchema, SShapeElementSchema } from "../../../src";
import createContainer from "./di.config";

function loadFile(path: string): Promise<string> {
Expand Down Expand Up @@ -53,7 +53,33 @@ export default function runMulticore() {
id: 'tiger',
position: { x: 400, y: 0 },
code: tiger
} as ShapedPreRenderedElementSchema
} as ShapedPreRenderedElementSchema,
{
type: 'foreign-object',
id: 'direct-html',
position: { x: 50, y: 350 },
size: { height: 50, width: 190 },
code: "<p>This is a free-floating HTML paragraph!</p>"
} as ForeignObjectElementSchema,
{
id: 'foreign-object-in-shape',
type: 'node',
position: {
x: 50,
y: 90
},
size: {
height: 60,
width: 160
},
children: [
{
type: 'child-foreign-object',
id: 'foreign-object-in-shape-contents',
code: "<div>This is <em>HTML</em> within <u>an SVG rectangle</u>!</div>"
} as ForeignObjectElementSchema
]
} as SShapeElementSchema
]
};

Expand Down
4 changes: 2 additions & 2 deletions examples/svg/svg-prerendered.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sprotty SVG Example</title>
<title>Sprotty Prerendered SVG / HTML Example</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<link rel="stylesheet" href="css/page.css">
<!-- support Microsoft browsers -->
Expand All @@ -13,7 +13,7 @@
<div class="container">
<div class="row" id="sprotty-app" data-app="svg">
<div class="col-md-10">
<h1>Sprotty SVG Example</h1>
<h1>Sprotty Prerendered SVG / HTML Example</h1>
</div>
<div class="help col-md-2">
<a href='https://github.com/theia-ide/sprotty/wiki/Using-sprotty'>Help</a>
Expand Down
2 changes: 1 addition & 1 deletion src/base/ui-extensions/ui-extension-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { IUIExtension } from "./ui-extension";
export class UIExtensionRegistry extends InstanceRegistry<IUIExtension> {
constructor(@multiInject(TYPES.IUIExtension) @optional() extensions: (IUIExtension)[] = []) {
super();
extensions.forEach((extension) => this.register(extension.id, extension));
extensions.forEach((extension) => this.register(extension.id(), extension));
}
}

Expand Down
13 changes: 7 additions & 6 deletions src/base/ui-extensions/ui-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { ViewerOptions } from "../views/viewer-options";
* A UI extension displaying additional UI elements on top of a sprotty diagram.
*/
export interface IUIExtension {
readonly id: string;
id(): string;
show(root: Readonly<SModelRoot>, ...contextElementIds: string[]): void;
hide(): void;
}
Expand All @@ -36,11 +36,12 @@ export abstract class AbstractUIExtension implements IUIExtension {
@inject(TYPES.ViewerOptions) protected options: ViewerOptions;
@inject(TYPES.ILogger) protected logger: ILogger;

abstract readonly id: string;
abstract readonly containerClass: string;
protected containerElement: HTMLElement;
protected activeElement: Element | null;

abstract id(): string;
abstract containerClass(): string;

show(root: Readonly<SModelRoot>, ...contextElementIds: string[]): void {
this.activeElement = document.activeElement;
if (!this.containerElement) {
Expand Down Expand Up @@ -78,11 +79,11 @@ export abstract class AbstractUIExtension implements IUIExtension {
}

protected getOrCreateContainer(baseDivId: string): HTMLElement {
let container = document.getElementById(this.id);
let container = document.getElementById(this.id());
if (container === null) {
container = document.createElement('div');
container.id = baseDivId + "_" + this.id;
container.classList.add(this.containerClass);
container.id = baseDivId + "_" + this.id();
container.classList.add(this.containerClass());
}
return container;
}
Expand Down
10 changes: 6 additions & 4 deletions src/features/command-palette/command-palette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ export class CommandPalette extends AbstractUIExtension {
static readonly ID = "command-palette";
static readonly isInvokePaletteKey = (event: KeyboardEvent) => matchesKeystroke(event, 'Space', 'ctrl');

readonly id = CommandPalette.ID;
readonly containerClass = "command-palette";

protected loadingIndicatorClasses = ['loading'];
protected xOffset = 20;
protected yOffset = 20;
Expand All @@ -64,6 +61,9 @@ export class CommandPalette extends AbstractUIExtension {
@inject(TYPES.DOMHelper) protected domHelper: DOMHelper;
@inject(MousePositionTracker) protected mousePositionTracker: MousePositionTracker;

public id() { return CommandPalette.ID; }
public containerClass() { return "command-palette"; }

show(root: Readonly<SModelRoot>, ...contextElementIds: string[]) {
super.show(root, ...contextElementIds);
this.paletteIndex = 0;
Expand Down Expand Up @@ -168,7 +168,9 @@ export class CommandPalette extends AbstractUIExtension {
}

protected onLoaded(success: 'success' | 'error') {
this.containerElement.removeChild(this.loadingIndicator);
if (this.containerElement.contains(this.loadingIndicator)) {
this.containerElement.removeChild(this.loadingIndicator);
}
}

protected renderLabeledActionSuggestion(item: LabeledAction, value: string) {
Expand Down
Loading