From 5cf9917dd5d5cb80c2e5d3e6e6258732b6131377 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Tue, 24 Mar 2020 09:25:17 +0100 Subject: [PATCH] Add support for foreignObject https://github.com/eclipse/sprotty/issues/169 --- examples/svg/src/di.config.ts | 6 ++- examples/svg/src/standalone.ts | 13 +++-- examples/svg/svg-prerendered.html | 4 +- .../{generic-views.ts => generic-views.tsx} | 34 ++++++++++++-- src/lib/model.ts | 47 +++++++++++++++++-- 5 files changed, 90 insertions(+), 14 deletions(-) rename src/lib/{generic-views.ts => generic-views.tsx} (52%) diff --git a/examples/svg/src/di.config.ts b/examples/svg/src/di.config.ts index 16a42cc8..a9ff339c 100644 --- a/examples/svg/src/di.config.ts +++ b/examples/svg/src/di.config.ts @@ -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 @@ -17,7 +17,8 @@ import { Container, ContainerModule } from "inversify"; import { TYPES, ConsoleLogger, LogLevel, loadDefaultModules, LocalModelSource, PreRenderedView, - SvgViewportView, ViewportRootElement, ShapedPreRenderedElement, configureModelElement + SvgViewportView, ViewportRootElement, ShapedPreRenderedElement, configureModelElement, + ForeignObjectElement, ForeignObjectView } from "../../../src"; export default () => { @@ -31,6 +32,7 @@ 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); }); const container = new Container(); diff --git a/examples/svg/src/standalone.ts b/examples/svg/src/standalone.ts index 6636ed44..1a76e793 100644 --- a/examples/svg/src/standalone.ts +++ b/examples/svg/src/standalone.ts @@ -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 @@ -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 } from "../../../src"; import createContainer from "./di.config"; function loadFile(path: string): Promise { @@ -53,7 +53,14 @@ 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: "

This is a free-floating HTML paragraph!

" + } as ForeignObjectElementSchema ] }; diff --git a/examples/svg/svg-prerendered.html b/examples/svg/svg-prerendered.html index d8685b04..9ef70721 100644 --- a/examples/svg/svg-prerendered.html +++ b/examples/svg/svg-prerendered.html @@ -3,7 +3,7 @@ - Sprotty SVG Example + Sprotty Prerendered SVG / HTML Example @@ -13,7 +13,7 @@
-

Sprotty SVG Example

+

Sprotty Prerendered SVG / HTML Example

Help diff --git a/src/lib/generic-views.ts b/src/lib/generic-views.tsx similarity index 52% rename from src/lib/generic-views.ts rename to src/lib/generic-views.tsx index 422a410b..4c960f38 100644 --- a/src/lib/generic-views.ts +++ b/src/lib/generic-views.tsx @@ -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 @@ -14,12 +14,15 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +/** @jsx svg */ +import { injectable } from "inversify"; +import { svg } from 'snabbdom-jsx'; import virtualize from "snabbdom-virtualize/strings"; import { VNode } from "snabbdom/vnode"; import { IView, RenderingContext } from "../base/views/view"; -import { setNamespace } from "../base/views/vnode-utils"; -import { PreRenderedElement } from "./model"; -import { injectable } from "inversify"; +import { setNamespace, setAttr } from "../base/views/vnode-utils"; +import { ForeignObjectElement, PreRenderedElement } from "./model"; +import { getSubType } from "../base/model/smodel-utils"; @injectable() export class PreRenderedView implements IView { @@ -35,3 +38,26 @@ export class PreRenderedView implements IView { } } + +/** + * An SVG `foreignObject` view with a namespace specified by the provided `ForeignObjectElement`. + * Note that `foreignObject` may not be supported by all browsers or SVG viewers. + */ +@injectable() +export class ForeignObjectView implements IView { + render(model: ForeignObjectElement, context: RenderingContext): VNode { + const foreignObjectContents = virtualize(model.code); + const node = + + {foreignObjectContents} + + {context.renderChildren(model)} + ; + setAttr(node, 'class', model.type); + const subType = getSubType(model); + if (subType) setAttr(node, 'class', subType); + setNamespace(foreignObjectContents, model.namespace); + return node; + } +} diff --git a/src/lib/model.ts b/src/lib/model.ts index 133e620b..88cad2be 100644 --- a/src/lib/model.ts +++ b/src/lib/model.ts @@ -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 @@ -15,8 +15,8 @@ ********************************************************************************/ import { SModelRoot, SModelRootSchema, SChildElement, SModelElementSchema } from "../base/model/smodel"; -import { Point, Dimension, ORIGIN_POINT, EMPTY_DIMENSION, Bounds } from "../utils/geometry"; -import { BoundsAware, boundsFeature, Alignable, alignFeature } from "../features/bounds/model"; +import { Point, Dimension, ORIGIN_POINT, EMPTY_DIMENSION, Bounds, isValidDimension, EMPTY_BOUNDS } from "../utils/geometry"; +import { BoundsAware, boundsFeature, Alignable, alignFeature, isBoundsAware } from "../features/bounds/model"; import { Locateable, moveFeature } from "../features/move/model"; import { Selectable, selectFeature } from "../features/select/model"; import { SNode, SPort } from '../graph/sgraph'; @@ -136,3 +136,44 @@ export class ShapedPreRenderedElement extends PreRenderedElement implements Boun } } + +/** + * A `foreignObject` element to be transferred to the DOM within the SVG. + * + * This can be useful to to benefit from e.g. HTML rendering features, such as line wrapping, inside of + * the SVG diagram. Note that `foreignObject` is not supported by all browsers and SVG viewers may not + * support rendering the `foreignObject` content. + * + * If no dimensions are specified in the schema element, this element will obtain the dimension of + * its parent to fill the entire available room. Thus, this element requires specified bounds itself + * or bounds to be available for its parent. + */ +export class ForeignObjectElement extends ShapedPreRenderedElement { + namespace: string; + get bounds(): Bounds { + if (isValidDimension(this.size)) { + return { + x: this.position.x, + y: this.position.y, + width: this.size.width, + height: this.size.height + }; + } else if (isBoundsAware(this.parent)) { + return { + x: this.position.x, + y: this.position.y, + width: this.parent.bounds.width, + height: this.parent.bounds.height + }; + } + return EMPTY_BOUNDS; + } +} + +/** + * Serializable schema for ForeignObjectElement. + */ +export interface ForeignObjectElementSchema extends ShapedPreRenderedElementSchema { + /** The namespace to be assigned to the elements inside of the `foreignObject`. */ + namespace: string +}