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 JSONForms Preview #11

Merged
merged 1 commit into from
Mar 7, 2019
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
565 changes: 565 additions & 0 deletions jsonforms-tooling-common/package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions jsonforms-tooling-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
"@jsonforms/core": "^2.2.0",
"@jsonforms/react": "^2.2.0",
"ajv": "^6.5.5",
"yeoman-environment": "^2.3.4",
"react": "^16.8.3",
"redux": "^3.0.0"
"redux": "^3.0.0",
"chokidar": "^2.1.2",
"yeoman-environment": "^2.3.4"
},
"type-check": "tsc",
"devDependencies": {
"@types/chokidar": "^1.7.5",
"rimraf": "^2.6.2"
}
}
228 changes: 227 additions & 1 deletion jsonforms-tooling-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import { generateDefaultUISchema } from '@jsonforms/core';
import { readFile, writeFile } from 'fs';
import Ajv from 'ajv';
import { sep } from 'path';
import { join, sep } from 'path';
import { watch } from 'chokidar';
const yeoman = require('yeoman-environment');

export enum Project {
Expand Down Expand Up @@ -67,6 +68,85 @@ export const generateUISchema = (editorInstance: any, path: string) => {
}
};

/**
* Shows a preview form of a given json schema and ui schema in a new panel inside the editor
* @param {any} editorInstance the instance of the editor
* @param {string} path the path to the schema or ui-schema file
* @param {string} extensionPath the path to the extension directory
*/
export const showPreview = (editorInstance: any, path: any, extensionPath: string) => {
if (!path) {
editorInstance.window.showOpenDialog(editorInstance.OpenDialogOptions = {
canSelectMany: false,
canSelectFolders: false,
canSelectFiles: true,
openLabel: 'Select ui schema',
filters: {
'Json Files': ['json'],
},
}).then((uiSchemafileUri: any) => {
if (uiSchemafileUri && uiSchemafileUri[0].fsPath) {
editorInstance.window.showOpenDialog(editorInstance.OpenDialogOptions = {
canSelectMany: false,
canSelectFolders: false,
canSelectFiles: true,
openLabel: 'Select schema',
filters: {
'Json Files': ['json'],
},
}).then((schemaFileUri: any) => {
if (schemaFileUri && schemaFileUri[0].fsPath) {
const uiSchemaPath = uiSchemafileUri[0].fsPath;
const schemaPath = schemaFileUri[0].fsPath;
showWebview(editorInstance, 'preview', extensionPath, uiSchemaPath, schemaPath);
} else {
showMessage('Please select a json schema file', 'err');
return;
}
});
} else {
showMessage('Please select a ui schema file', 'err');
return;
}
});
} else {
editorInstance.window.showQuickPick(['UI Schema', 'Schema'], editorInstance.QuickPickOptions = {
canSelectMany: false,
placeHolder: 'Was that the UI schema or the schema file?'
}).then((schema: any) => {
if (schema) {
let selectLabel = 'Select ui Schema';
if (schema === 'UI Schema') {
selectLabel = 'Select Schema';
}
editorInstance.window.showOpenDialog(editorInstance.OpenDialogOptions = {
canSelectMany: false,
canSelectFolders: false,
canSelectFiles: true,
openLabel: selectLabel,
filters: {
'Json Files': ['json'],
},
}).then((schemaFileUri: any) => {
if (schemaFileUri && schemaFileUri[0].fsPath) {
if (schema === 'UI Schema') {
showWebview(editorInstance, 'preview', extensionPath, path, schemaFileUri[0].fsPath);
} else {
showWebview(editorInstance, 'preview', extensionPath, schemaFileUri[0].fsPath, path);
}
} else {
showMessage('Please select a json schema file', 'err');
return;
}
});
} else {
showMessage('Please select the schema type', 'err');
return;
}
});
}
};

/**
* Async Generate UI Schema
* @param {any} editorInstance the instance of the editor
Expand Down Expand Up @@ -206,3 +286,149 @@ const cloneAndInstall = (editorInstance: any, project: string, path: string, nam
});
});
};

/**
* Get HTML to be shown inside the preview webview
* @param {any} scriptUriCore Uri of jsonforms-core.js
* @param {any} scriptUriReact Uri of jsonforms-react.js
* @param {any} scriptUriMaterial Uri of jsonforms-material.js
* @param {JSON} schema schema of the form
* @param {JSON} uiSchema uiSchema of the form
*/
const getPreviewHTML = (
scriptUriCore: any,
scriptUriReact: any,
scriptUriMaterial: any,
schema: string,
uiSchema: string
) => {
return `<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/redux.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/react-redux.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/browser.min.js"></script>
<script src="${scriptUriCore}"></script>
<script src="${scriptUriReact}"></script>
<script src="${scriptUriMaterial}"></script>
</head>
<body style="background: #fff">
<div id="root">Loading...</div>
<script type="text/babel">
var schema = ${schema};
var uiSchema = ${uiSchema};
const store = Redux.createStore(
Redux.combineReducers({ jsonforms: JSONFormsCore.jsonformsReducer() }),
{
jsonforms: {
fields: JSONFormsMaterial.materialFields,
renderers: JSONFormsMaterial.materialRenderers
},
}
);
store.dispatch(JSONFormsCore.Actions.init({}, schema, uiSchema));
const mapStateToProps = state => {
return { dataAsString: JSON.stringify(JSONFormsCore.getData(state), null, 2) }
};
var App = ({ dataAsString }) => {
return (
<div>
<JSONFormsReact.JsonForms />
</div>
);
};
const CApp = ReactRedux.connect(mapStateToProps, null)(App);
ReactDOM.render(
<ReactRedux.Provider store={store}>
<CApp />
</ReactRedux.Provider>,
document.getElementById('root')
);
</script>
</body>
</html>`;
};

/**
* Show webview
* @param {any} editorInstance the instance of the editor
* @param {string} id the id for the webview
* @param {string} extensionPath the path to the extension directory
* @param {string} uiSchemaPath the path to the ui schema
* @param {string} schemaPath the path to the schema
*/
const showWebview = (
editorInstance: any,
id: string,
extensionPath: string,
uiSchemaPath: string,
schemaPath: string
) => {
const name = id;
const webView = editorInstance.window.createWebviewPanel(
'view-' + name,
name,
editorInstance.ViewColumn.Two,
{ enableScripts: true}
);
preparePreview(editorInstance, extensionPath, uiSchemaPath, schemaPath, (html: string) => {
webView.webview.html = html;
watch(uiSchemaPath).on('change', (event: any, path: any) => {
preparePreview(editorInstance, extensionPath, uiSchemaPath, schemaPath, (newHtml: string) => {
webView.webview.html = newHtml;
});
});
});
};

/**
* Prepare the preview webview
* @param {any} editorInstance the instance of the editor
* @param {string} extensionPath the path to the extension directory
* @param {string} uiSchemaPath the path to the ui schema
* @param {string} schemaPath the path to the schema
* @param {any} callback the callback, that is called, after all files are loaded
*/
const preparePreview = (
editorInstance: any,
extensionPath: string,
uiSchemaPath: string,
schemaPath: string,
callback: any
) => {
// Prepare the scripts needed to show the App inside the Webview
const scriptPathOnDiskCore = editorInstance.Uri.file(
join(extensionPath, 'assets', 'preview', 'jsonforms-core.js')
);
const scriptPathOnDiskReact = editorInstance.Uri.file(
join(extensionPath, 'assets', 'preview', 'jsonforms-react.js')
);
const scriptPathOnDiskMaterial = editorInstance.Uri.file(
join(extensionPath, 'assets', 'preview', 'jsonforms-material.js')
);
const scriptUriCore = scriptPathOnDiskCore.with({ scheme: 'vscode-resource'});
const scriptUriReact = scriptPathOnDiskReact.with({ scheme: 'vscode-resource'});
const scriptUriMaterial = scriptPathOnDiskMaterial.with({ scheme: 'vscode-resource'});

// Read json files and load html for webview
readFile(schemaPath, 'utf8', (readError, schema) => {
if ((readError !== null) && readError.message) {
showMessage(editorInstance, readError.message, 'err');
return;
}
readFile(uiSchemaPath, 'utf8', (secondReadError, uiSchema) => {
if ((secondReadError !== null) && secondReadError.message) {
showMessage(editorInstance, secondReadError.message, 'err');
return;
}
callback(getPreviewHTML(scriptUriCore, scriptUriReact, scriptUriMaterial, schema, uiSchema));
});
});
};
17 changes: 16 additions & 1 deletion theia-plugin/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import * as theia from '@theia/plugin';
import { createProject, generateUISchema, Project } from 'jsonforms-tooling-common';
import { createProject, generateUISchema, Project, showPreview } from 'jsonforms-tooling-common';

export const start = (context: theia.PluginContext) => {
const createExampleProjectCommandOptions = {
Expand Down Expand Up @@ -41,7 +41,22 @@ export const start = (context: theia.PluginContext) => {
}
);

const showPreviewCommandOptions = {
id: 'show-preview',
label: 'JSONForms: Show Preview',
};
const showPreviewCommand = theia.commands.registerCommand(
showPreviewCommandOptions,
(args: any) => {
if (args === undefined) {
args = {fsPath: null};
}
showPreview(theia, args.fsPath, context.extensionPath);
}
);

context.subscriptions.push(createExampleProjectCommand);
context.subscriptions.push(createSeedProjectCommand);
context.subscriptions.push(generateUISchemaCommand);
context.subscriptions.push(showPreviewCommand);
};
Loading