Skip to content

Commit

Permalink
Improved JSX factory
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Bicker <[email protected]>
  • Loading branch information
jbicker committed Feb 20, 2024
1 parent 2cdca53 commit 354f4e0
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 4 deletions.
3 changes: 3 additions & 0 deletions examples/browser-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import runRandomGraphDistributed from "./random-graph-distributed/src/standalone
import runSvgPreRendered from "./svg/src/standalone";
import runMulticore from "./multicore/src/multicore";
import runFlowchart from "./flowchart/src/standalone";
import runJsxample from "./jsxample/src/standalone";

const appDiv = document.getElementById('sprotty-app');
if (appDiv) {
Expand All @@ -41,6 +42,8 @@ if (appDiv) {
runMulticore();
else if (appMode === 'flowchart')
runFlowchart();
else if (appMode === 'jsxample')
runJsxample();
else
throw new Error('Dunno what to do :-(');
}
2 changes: 2 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ <h3>Without Server</h3>
If you zoom in, the communication channels between the cores appear.</li>
<li><a href="flowchart/flowchart.html">Flowchart:</a><br>
A flowchart with custom views for nodes and edges. The labels on nodes and edges are editable and the nodes and edges are moveable.</li>
<li><a href="jsxample/jsxample.html">JSXample:</a><br>
A demonstration how to use React alike components in Sprotty views.</li>
</ul>

<h3>With Server</h3>
Expand Down
38 changes: 38 additions & 0 deletions examples/jsxample/css/diagram.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/********************************************************************************
* Copyright (c) 2024 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
* 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
********************************************************************************/

:root {
--black: #000;
--white: #fff;
--stroke-normal: 2;
--stroke-medium: 3;
--stroke-bold: 5;
--red-500: #f44336;
--yellow-500: #ffeb3b;
--amber-500: #ffc107;
}

/* graph */
.sprotty-graph {
font-size: 15pt;
}

/* nodes */
.sprotty-node {
stroke: var(--black);
stroke-width: var(--stroke-normal);
fill: var(--white);
}
50 changes: 50 additions & 0 deletions examples/jsxample/css/page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/********************************************************************************
* Copyright (c) 2024 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
* 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
********************************************************************************/


.footer {
margin-top: 10px;
text-align: right;
font-size: 10px;
color: #888;

display: flex;
justify-content: space-between;
}

.shortcuts {
text-align: left;
}

.copyright {
text-align: right;
}

.help {
margin-top: 24px;
text-align: right;
font-size: 16px;
color: #888;
}

svg {
margin-top: 15px;
width: 100%;
height: 600px;
border-style: solid;
border-width: 1px;
border-color: #bbb;
}
57 changes: 57 additions & 0 deletions examples/jsxample/jsxample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sprotty JSXample</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="css/page.css">
<!-- support Microsoft browsers -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dom4/3.0.0/dom4.js">
<script>
document.addEventListener("DOMContentLoaded", function toggleShortcuts(){
var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
var mac = document.getElementById('mac');
var other = document.getElementById('other');
if (isMac) {
mac.style.display = 'block';
other.style.display = 'none';
} else {
mac.style.display = 'none';
other.style.display = 'block';
}
});
</script>
</head>
<body>
<div class="container">
<div class="row" id="sprotty-app" data-app="jsxample">
<div class="col-md-10">
<h1>Sprotty JSXample</h1>
</div>
<div class="help col-md-2">
<a href="https://sprotty.org/docs/user_interaction/">Help</a>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="sprotty" class="sprotty"></div>
</div>
<div class="footer">
<div id="mac" class="shortcuts">
<b>Fit to screen:</b> Cmd+Shift+F, <b>Center selected:</b> Cmd+Shift+C, <b>Export SVG:</b> Cmd+Shift+E
</div>
<div id="other" class="shortcuts" style="display: none;">
<b>Fit to screen:</b> Ctrl+Shift+F, <b>Center selected:</b> Ctrl+Shift+C, <b>Export SVG:</b> Ctrl+Shift+E
</div>
<div class="copyright">
&copy; 2024 <a href="https://www.typefox.io/">TypeFox GmbH</a>.
</div>
</div>
</div>
</div>
<script src="../resources/bundle.js"></script>
</body>
</html>
56 changes: 56 additions & 0 deletions examples/jsxample/src/di.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/********************************************************************************
* Copyright (c) 2024 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
* 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 { Container, ContainerModule } from "inversify";
import {
ConsoleLogger,
LocalModelSource,
LogLevel,
SGraphImpl,
SGraphView,
SLabelImpl,
SLabelView,
SNodeImpl,
TYPES,
configureModelElement,
configureViewerOptions,
loadDefaultModules} from "sprotty";
import { ExampleNodeView } from "./views";

export default (containerId: string) => {
require('../css/diagram.css');

const jsxampleModule = new ContainerModule((bind, unbind, isBound, rebind) => {
bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope();
rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope();
rebind(TYPES.LogLevel).toConstantValue(LogLevel.log);

const context = { bind, unbind, isBound, rebind };

configureModelElement(context, 'graph', SGraphImpl, SGraphView);
configureModelElement(context, 'node', SNodeImpl, ExampleNodeView);
configureModelElement(context, 'label', SLabelImpl, SLabelView);

configureViewerOptions(context, {
needsClientLayout: true,
});
});

const container = new Container();
loadDefaultModules(container);
container.load(jsxampleModule);
return container;
};
55 changes: 55 additions & 0 deletions examples/jsxample/src/standalone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/********************************************************************************
* Copyright (c) 2024 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
* 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 { LocalModelSource, TYPES } from 'sprotty';
import createContainer from './di.config';

export default async function runJsxample() {
const container = createContainer('sprotty');
const modelSource = container.get<LocalModelSource>(TYPES.ModelSource);

const graph = {
id: 'root',
type: 'graph',
children: [
{
id: 'node1',
type: 'node',
position: { x: 100, y: 100 },
layout: 'stack',
layoutOptions: {
hAlign: 'center',
vAlign: 'center',
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
minWidth: 300,
minHeight: 100
},
children: [
{
id: 'label1',
type: 'label',
text: 'View enhanced with JSX function components.',
position: { x: 150, y: 50 }
}
]
}
]
};
modelSource.setModel(graph);
}
69 changes: 69 additions & 0 deletions examples/jsxample/src/views.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/********************************************************************************
* Copyright (c) 2024 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
* 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
********************************************************************************/
/** @jsx svg */
import { SLabelImpl, svg } from 'sprotty';

import { injectable } from "inversify";
import { VNode } from "snabbdom";
import { IViewArgs, RenderingContext, SShapeElementImpl, ShapeView } from "sprotty";

@injectable()
export class ExampleNodeView extends ShapeView {
override render(node: Readonly<SShapeElementImpl>, context: RenderingContext, args?: IViewArgs): VNode | undefined {
if (!this.isVisible(node, context)) {
return undefined;
}

return <g>
<SVGRectComponent
name='I am a header created by a jsx function component' colour='#BBB'
position={{ x: 0, y: 0 }} size={{ width: node.size.width, height: 20 }} />
{SVGRectComponent({
colour: '#999',
position: { x: 0, y: node.size.height - 20 }, size: { width: node.size.width, height: 20 }
})}
<rect
x="0"
y="0"
fill="none"
stroke="#000"
stroke-width="5"
width={node.size.width}
height={node.size.height}></rect>
{context.renderChildren(node)}
</g>;
}
}

export class ExampleLabelView extends ShapeView {
override render(node: Readonly<SLabelImpl>, context: RenderingContext, args?: IViewArgs): VNode | undefined {
return <text x={node.position.x - (node.size.width / 2)} y={node.position.y}>{node.text}</text>;
}
}

interface TestProps {
name?: string;
colour: string;
position: { x: number, y: number };
size?: { width: number, height: number };
}

function SVGRectComponent(props: TestProps) {
return <g>
<rect fill={props.colour} x={props.position.x} y={props.position.y} width={props.size?.width ?? 30} height={props.size?.height ?? 30} />
{props.name ? <text font-size="12" x={props.position.x + 5} y={props.position.y + 15}>{props.name}</text> : ''}
</g>;
}
16 changes: 12 additions & 4 deletions packages/sprotty/src/lib/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,14 @@ function normalizeAttrs(source: VNodeData | null, defNS: string, namespace?: str
Object.keys(source).forEach(key => {
if (key === 'key' || key === 'classNames' || key === 'selector') return;
const idx = key.indexOf('-');
if (idx > 0)
addAttr(key.slice(0, idx), key.slice(idx + 1), source[key]);
else if (!data[key])
if (idx > 0) {
const modname = key.slice(0, idx);
if (modulesNS.includes(modname)) {
addAttr(modname, key.slice(idx + 1), source[key]);
} else {
addAttr(defNS, key, source[key]);
}
} else if (!data[key])
addAttr(defNS, key, source[key]);
});
return data;
Expand All @@ -63,7 +68,10 @@ function normalizeAttrs(source: VNodeData | null, defNS: string, namespace?: str

// eslint-disable-next-line @typescript-eslint/naming-convention
function JSX(namespace?: string, defNS: string = 'props') {
return (tag: FunctionComponent | string, attrs: VNodeData | null, ...children: JsxVNodeChild[]) => jsx(tag, normalizeAttrs(attrs, defNS, namespace), children);
return (tag: FunctionComponent | string, attrs: VNodeData | null, ...children: JsxVNodeChild[]) => {
const isComponent = typeof tag === 'function';
return jsx(tag, (isComponent ? attrs : normalizeAttrs(attrs, defNS, namespace)), children);
};
}

const html = JSX();
Expand Down

0 comments on commit 354f4e0

Please sign in to comment.