├── README.MD ├── pom.xml └── src └── main ├── java └── sample │ └── reactmaterial │ ├── PersistanceDummy.java │ ├── ReactMaterialUITestApp.java │ └── ReactMaterialUITestSession.java └── web ├── client ├── css │ └── index.css ├── dialog.jsx ├── global.jsx ├── greeter.jsx ├── index.html ├── index.jsx ├── materialui │ └── materialplay.jsx ├── static.jsx └── util.jsx ├── jnpm.kson ├── package-lock.json └── package.json /README.MD: -------------------------------------------------------------------------------- 1 | # React JSX front end app powered by a java backend, no node/npm/webpack required 2 | 3 | ## Frontend 4 | 5 | * **intrinsic react jsx transpilation** 6 | * Ultra fast **hot reloading / live editing** (Disclaimer: breakpoints cannot be set inside Chrome then: use **'debugger;'** instead) 7 | * built-in **npm replacement** dependency management ('JNPM') fetches missing imports on demand 8 | * integrated **bundler/transpiler**. Integrates **google-clojure compiler** to compile down to **ES5** in production mode 9 | * Sophisticated **java/javascript interoperation**. Fulfil Promises from js by java code, server push by calling callbacks from java transparently to js client code. 10 | 11 | Has been tested with major react-UI libs: react-material-ui, react-bootstrap and react-semantic-ui (should work with any npm powered js lib) 12 | 13 | ## Server Side 14 | 15 | * Actor based async server (Java 8, node style threading model, but enables multithreading by assigning client sessions to additional threads) 16 | * (optional) Session handling, timeout, ressurrection (SPA client was away/offline and connects back) 17 | * Dynamically change connection type (http adaptive long poll or websockets) without code change 18 | 19 | *Very fast Live Editing / Live Reloading (updates within milliseconds after hitting 'Ctrl-S'):* 20 | 21 | [![Watch the video](http://img.youtube.com/vi/IrdrKoP8WD8/0.jpg)](http://youtu.be/IrdrKoP8WD8) 22 | 23 | *Jnpm in action, session resurrection:* 24 | 25 | [![Watch the video](http://img.youtube.com/vi/CuYuynl16o4/0.jpg)](http://youtu.be/CuYuynl16o4) 26 | 27 | ## Getting started 28 | 29 | [Starter templates and scaffolding](https://github.com/RuedigerMoeller/react-with-java-starter-templates) 30 | 31 | **Docs** 32 | 33 | [Kontraktor's JSX transpilation + npm emulation](https://github.com/RuedigerMoeller/kontraktor/wiki/Kontraktor-4-React-JSX) 34 | 35 | [Talking from JavaScript with the Java Server](https://github.com/RuedigerMoeller/kontraktor/wiki/Kontraktor-4-Http) 36 | 37 | [Examples for semantic-ui-react and react-bootstrap](https://github.com/RuedigerMoeller/kontraktor/tree/trunk/examples/webapp-spa/react-ui-lib-examples) 38 | 39 | [Kontraktor home](https://github.com/RuedigerMoeller/kontraktor) 40 | 41 | 42 | ## Run this sample app 43 | 44 | **IDE** 45 | * add project to your ide (from pom) 46 | * run ReactMaterialUITestApp with workingdir [...]/examples/webapp-spa/react-ui-lib-examples/react-material-ui 47 | * goto localhost:8080 in your browser 48 | * first request triggers download of required npm packages so give it like 50 seconds (wathc console) 49 | * set DEVMODE to false to get a (dynamically) bundled build 50 | 51 | **commandline:** 52 | ```bash 53 | git clone https://github.com/RuedigerMoeller/InstrinsicReactJSX.git 54 | cd InstrinsicReactJSX/ 55 | mvn clean package 56 | java -jar target/bundle.jar 57 | ``` 58 | browser: localhost:8080 (watch console on first request isntalling dependencies ..) 59 | 60 | **Note:** 61 | * on the initial request, all missing npm dependencies are installed by kontraktor-Jnpm, this migth take up to a minute, 62 | **once** (watch console) per install. 63 | * as source is kept in separate files during development mode, loading is somewhat slowish (~3 seconds). Set DEVMODE to false 64 | inside ReactMaterialUITestApp, restart server in order to see loading time of bundled and minified app. Even then: First request triggers bundling (cached then) as kontraktor-http favors dynamic optimization over 'pre-building' (since 4.18.5 : static build artifacts support). 65 | 66 | [Simple starter templates and scaffolding](https://github.com/RuedigerMoeller/react-with-java-starter-templates) 67 | 68 | ## Session resurrection 69 | 70 | * goto localhost:8080 login (just some dummy nick) 71 | * press greet button 72 | * restart server 73 | * press greet button again. 74 | 75 | Inner workings of resurrection: once a remote call to an unknown actor is received, a lookup is done from sessionid => userid (+pwd), then 76 | a new session actor is created. After that the remotecall is routed. Client receives a notification so it might update 77 | stale data. 78 | 79 | Of course a real world app might associate arbitrary session data with a session id. Session resurrection is important 80 | for SPA apps (especially on mobile) as one frequently loses connection or keeps an SPA open for days without "refreshing". 81 | 82 | 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | org.sonatype.oss 7 | oss-parent 8 | 7 9 | 10 | 11 | 4.0.0 12 | de.ruedigermoeller 13 | react-material-ui 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | 22 | 1.8 23 | 1.8 24 | 25 | 26 | 27 | 28 | maven-assembly-plugin 29 | 30 | bundle 31 | false 32 | 33 | jar-with-dependencies 34 | 35 | 36 | 37 | sample.reactmaterial.ReactMaterialUITestApp 38 | 39 | 40 | 41 | 42 | 43 | make-assembly 44 | 45 | package 46 | 47 | 48 | single 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | de.ruedigermoeller 61 | kontraktor-http 62 | 4.26.3 63 | 64 | 65 | 66 | junit 67 | junit 68 | 4.12 69 | test 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/sample/reactmaterial/PersistanceDummy.java: -------------------------------------------------------------------------------- 1 | package sample.reactmaterial; 2 | 3 | import org.nustaq.offheap.FSTAsciiStringOffheapMap; 4 | import org.nustaq.offheap.FSTUTFStringOffheapMap; 5 | 6 | import java.io.File; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class PersistanceDummy { 11 | 12 | // below a dummy implementation (save session data for resurrection in a mmapped file) 13 | protected FSTAsciiStringOffheapMap sessionDataCache; 14 | private int sessionDataIndex = 1; 15 | 16 | public PersistanceDummy() { 17 | try { 18 | sessionDataCache = new FSTAsciiStringOffheapMap( 19 | "./session.bin", 48, 1024L*1024*10, 100_000 20 | ); 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | public Object getSessionData(String sessionId) { 27 | Object[] dataAndTimeStamp = (Object[]) sessionDataCache.get(sessionId); 28 | if ( dataAndTimeStamp == null ) 29 | return null; 30 | return dataAndTimeStamp[0]; 31 | } 32 | 33 | // fade out old session data 34 | public void flipSessionCache() { 35 | // demo impl, need persistence to have clients survive a server restart 36 | // TODO: remove old sessions using timestamp 37 | // sessionDataCache.values().forEachRemaining(..); 38 | } 39 | 40 | public void putSessionData(String sessionid, Object data) { 41 | // implicitely moves to "new bucket" 42 | sessionDataCache.put(sessionid,new Object[] {data,System.currentTimeMillis()}); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/sample/reactmaterial/ReactMaterialUITestApp.java: -------------------------------------------------------------------------------- 1 | package sample.reactmaterial; 2 | 3 | import org.nustaq.kontraktor.Actor; 4 | import org.nustaq.kontraktor.IPromise; 5 | import org.nustaq.kontraktor.Scheduler; 6 | import org.nustaq.kontraktor.annotations.Local; 7 | import org.nustaq.kontraktor.impl.SimpleScheduler; 8 | import org.nustaq.kontraktor.remoting.base.SessionResurrector; 9 | import org.nustaq.kontraktor.remoting.encoding.SerializerType; 10 | import org.nustaq.kontraktor.remoting.http.undertow.Http4K; 11 | import org.nustaq.kontraktor.util.Log; 12 | import org.nustaq.kontraktor.webapp.javascript.clojure.ClojureJSPostProcessor; 13 | import org.nustaq.kontraktor.webapp.transpiler.JSXIntrinsicTranspiler; 14 | 15 | import java.io.File; 16 | import java.util.Random; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.stream.IntStream; 19 | 20 | /** 21 | * minimal implementation of session based server (incl. load balancing) 22 | */ 23 | public class ReactMaterialUITestApp extends Actor implements SessionResurrector { 24 | 25 | private Scheduler clientThreads[]; 26 | private Random rand = new Random(); 27 | private PersistanceDummy persistance = new PersistanceDummy(); 28 | 29 | @Local 30 | public void init(int nthreads) { 31 | clientThreads = new Scheduler[nthreads]; 32 | IntStream.range(0,nthreads) 33 | .forEach( i -> clientThreads[i] = new SimpleScheduler(100, true /*Important!*/ )); 34 | cycle(); 35 | } 36 | 37 | public IPromise login(String username) { 38 | if ( "".equals(username.trim()) ) { 39 | return reject("empty username"); 40 | } 41 | ReactMaterialUITestSession session = AsActor( 42 | ReactMaterialUITestSession.class, 43 | // randomly distribute session actors among clientThreads 44 | clientThreads[rand.nextInt(clientThreads.length)] 45 | ); 46 | session.init(username,self()); 47 | return resolve(session); // == new Promise(session) 48 | } 49 | 50 | @Override @Local 51 | public IPromise reanimate(String sessionId, long remoteRefId) { 52 | // dummy in memory 53 | String userName = (String) persistance.getSessionData(sessionId); 54 | if ( userName != null ) { 55 | // create a new session with stored data, client is notified 56 | // in case it needs to refresh client side data 57 | Log.Info(this,"reanimated session "+sessionId+" with data "+userName); 58 | return (IPromise)login(userName); 59 | } 60 | return resolve(null); // cannot reanimate => client shows "session expired" 61 | } 62 | 63 | void cycle() { 64 | if ( ! isStopped() ) { 65 | // sessions are remembered fo 5 days 66 | delayed(TimeUnit.DAYS.toMillis(5), () -> { 67 | persistance.flipSessionCache(); 68 | cycle(); 69 | }); 70 | } 71 | } 72 | 73 | @Local 74 | public void registerSessionData(String id, Object data) { 75 | Log.Info(this,"session "+id+" is "+data); 76 | persistance.putSessionData(id,data); 77 | } 78 | 79 | public static void main(String[] args) { 80 | // provide any argument for PRODMODE 81 | boolean DEVMODE = true && (args == null || args.length == 0) ; 82 | 83 | if ( ! new File("./src/main/web/client/index.html").exists() ) { 84 | System.out.println("Please run with working dir: '[..]/react-material-ui"); 85 | System.exit(-1); 86 | } 87 | 88 | ReactMaterialUITestApp app = AsActor(ReactMaterialUITestApp.class); 89 | app.init(4); 90 | 91 | Http4K.Build("localhost", 8080) 92 | .resourcePath("/") 93 | .elements("./src/main/web/client","./src/main/web/lib","./src/main/web/node_modules") 94 | .transpile("jsx", 95 | new JSXIntrinsicTranspiler(DEVMODE) 96 | .configureJNPM("./src/main/web/node_modules","./src/main/web/jnpm.kson") 97 | .autoJNPM(true) 98 | .hmr(DEVMODE) // support required for hot reloading 99 | ) 100 | .allDev(DEVMODE) 101 | .jsPostProcessors(new ClojureJSPostProcessor()) // uses google clojure transpiler to ES5 (PRODMODE only) 102 | // (PRODMODE only: look (and create if not present) static build artefact for budled index.html [avoids rebundling on first request in prodmode] 103 | // Warning: you need to delete this file in order to force a rebuild then 104 | .productionBuildDir(new File("./dist") ) 105 | .buildResourcePath() 106 | .httpAPI("/api", app) 107 | .serType(SerializerType.JsonNoRef) 108 | .setSessionTimeout(10_000) // extra low to showcase session resurrection 109 | .buildHttpApi() 110 | .websocket("/ws",app) 111 | .serType(SerializerType.JsonNoRef) 112 | .sendSid(true) 113 | .buildWebsocket() 114 | .hmrServer(DEVMODE) // hot reloading file tracking server 115 | .build(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/sample/reactmaterial/ReactMaterialUITestSession.java: -------------------------------------------------------------------------------- 1 | package sample.reactmaterial; 2 | 3 | import org.nustaq.kontraktor.Actor; 4 | import org.nustaq.kontraktor.IPromise; 5 | import org.nustaq.kontraktor.Promise; 6 | import org.nustaq.kontraktor.remoting.base.RemotedActor; 7 | 8 | public class ReactMaterialUITestSession extends Actor implements RemotedActor { 9 | 10 | private String name; 11 | private ReactMaterialUITestApp app; 12 | 13 | public void init(String name, ReactMaterialUITestApp app) { 14 | this.app = app; 15 | this.name = name; 16 | } 17 | 18 | public IPromise greet(String who) { 19 | return new Promise("Hello "+who+" from "+name); 20 | } 21 | 22 | /** 23 | * interface RemotedActor, session time out notification callback 24 | * @param connectionIdentifier 25 | */ 26 | @Override 27 | public void hasBeenUnpublished(String connectionIdentifier) { 28 | } 29 | 30 | @Override 31 | public void hasBeenPublished(String connectionIdentifier) { 32 | // associate user identity with sessionid for resurrection 33 | app.registerSessionData(connectionIdentifier,name); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/web/client/css/index.css: -------------------------------------------------------------------------------- 1 | /**empty**/ -------------------------------------------------------------------------------- /src/main/web/client/dialog.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class DialogExampleSimple extends React.Component { 4 | state = { 5 | open: false, 6 | }; 7 | 8 | handleOpen = () => { 9 | this.setState({open: true}); 10 | }; 11 | 12 | handleClose = () => { 13 | this.setState({open: false}); 14 | }; 15 | 16 | render() { 17 | const actions = [ 18 | , 23 | , 29 | ]; 30 | 31 | return ( 32 |
33 | 34 | 41 | The actions in this window were passed in as an array of React objects. 42 | 43 |
44 | ); 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/web/client/global.jsx: -------------------------------------------------------------------------------- 1 | import {KClient} from 'kontraktor-client' 2 | 3 | const global = { 4 | kclient: new KClient(), 5 | app: null, 6 | server: null, 7 | session: null 8 | }; 9 | 10 | export default global; -------------------------------------------------------------------------------- /src/main/web/client/greeter.jsx: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import global from "./global"; 3 | import {Fader} from "./util"; 4 | import {HCenter} from "./util"; 5 | import RaisedButton from 'material-ui/RaisedButton'; 6 | 7 | class Greeter extends Component { 8 | 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | greeting: '...' 13 | }; 14 | } 15 | 16 | componentDidMount() { 17 | global.session.greet('World') 18 | .then( (res,err) => this.setState({greeting: err ? err : res}) ); 19 | } 20 | 21 | anotherGreet() { 22 | if ( window._kreactapprender ) 23 | window._kreactapprender.forceUpdate(); 24 | global.session.greet("Another World "+new Date()) 25 | .then( (res,err) => this.setState({greeting: err ? err : res}) ); 26 | } 27 | 28 | render() { 29 | /*enforce component creation to trigger fading*/ 30 | return ( 31 |
32 | 33 | 34 | { this.state.greeting == '...' ? 35 |
{this.state.greeting}
36 | : 37 |
{this.state.greeting}
38 | } 39 |
40 |
41 |

42 | 43 | this.anotherGreet()}> 44 | Greet 45 | 46 | 47 |
48 | ); 49 | } 50 | 51 | } 52 | 53 | export default Greeter; -------------------------------------------------------------------------------- /src/main/web/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | react playground sample 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/web/client/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Component} from 'react' 3 | import ReactDOM from 'react-dom'; 4 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; 5 | import {KClientListener} from 'kontraktor-client'; 6 | import {HCenter,Fader} from './util'; 7 | import Greeter from './greeter'; 8 | import global from "./global" 9 | import MaterialPlay, {Dummy,Dummy1,DummyLambda} from './materialui/materialplay'; 10 | import TextField from 'material-ui/TextField'; 11 | import RaisedButton from 'material-ui/RaisedButton'; 12 | import Dialog from 'material-ui/Dialog'; 13 | import Static from './static'; 14 | import {RadioButtonGroup,RadioButton} from 'material-ui/RadioButton'; 15 | import Snackbar from 'material-ui/Snackbar'; 16 | import {KClient} from 'kontraktor-client'; 17 | 18 | class App extends Component { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | user: '', 24 | loginEnabled: false, 25 | loggedIn: false, 26 | relogin: false, 27 | connectionType: 'HTLP', 28 | error: null, 29 | snackText: "", 30 | snackOpen: false 31 | }; 32 | const self = this; 33 | global.kclient.listener = new class extends KClientListener { 34 | // session timeout or resurrection fail 35 | onInvalidResponse(response) { 36 | console.error("invalid response",response); 37 | self.setState({relogin: true}); // session expired 38 | } 39 | onResurrection() { 40 | console.log("session resurrected. should update client data + resubscribe streams in case !") 41 | self.setState({snackText: "Session Resurrected !", snackOpen: true }); 42 | } 43 | }; 44 | } 45 | handleConnectionSelect(event, value) { 46 | this.setState( { connectionType:"http" == value ? "HTPL" : "WS" } ) 47 | } 48 | 49 | handleUChange(ev) { 50 | this.setState( {user: ev.target.value}, () => this.validate() ); 51 | } 52 | 53 | validate() { 54 | this.setState({ 55 | loginEnabled: this.state.user.trim().length > 0 56 | }); 57 | } 58 | 59 | relogin() { 60 | // forcereload 61 | document.location.href = "/"; 62 | } 63 | 64 | login() { 65 | var url,connectionType; 66 | if ( this.state.connectionType == 'HTLP' ) { 67 | url = "/api"; 68 | connectionType = "HTLP"; 69 | } else { 70 | url = "ws://"+document.location.host+"/ws"; 71 | connectionType = "WS"; 72 | } 73 | global.kclient 74 | .connect(url,connectionType) 75 | .then( (server,err) => { 76 | if ( err ) 77 | this.setState( {error: ""+err} ); 78 | else { 79 | global.server = server; 80 | server.login( this.state.user ) 81 | .then( (session,err) => { 82 | if ( err ) 83 | this.setState( {error: ""+err} ); 84 | else { 85 | global.session = session; 86 | console.log("logged in"); 87 | this.setState({loggedIn:true}); 88 | } 89 | }) 90 | } 91 | }); 92 | } 93 | 94 | componentWillUpdate(nextProps,nextState) { 95 | if ( !this.state.loggedIn && nextState.loggedIn ) { 96 | console.log("will be logged in ..") 97 | } 98 | } 99 | 100 | render() { 101 | const actions = [ 102 | this.relogin() } 106 | /> 107 | ]; 108 | return ( 109 | 110 |
111 |

{"Dummy:"+Dummy()+" Dummy1:"+Dummy1()+" DummyLambda:"+DummyLambda()}

112 | this.setState({snackText: "", snackOpen: false }) } 117 | /> 118 | this.relogin()} 124 | > 125 | Session timed out. Pls relogin. 126 | 127 |


128 | 129 |
130 | Hello World ! 131 |
132 |
133 |
134 | { this.state.loggedIn ? 135 | 136 | : ( 137 | 138 | 139 | this.handleUChange(ev) } 141 | hintText="nickname" 142 | floatingLabelText="Login" 143 | /> 144 | 145 |
146 | 147 | this.handleConnectionSelect(ev,val)}> 148 | 152 | 156 | 157 | 158 |
159 | 160 | this.login(ev) }> 163 | Login 164 | 165 | 166 |
167 | ) 168 | } 169 |
170 | {this.state.error ?
error
: ""} 171 |
172 | { this.state.loggedIn ? 173 |
174 | 175 |
176 | : 177 | } 178 |
179 |
180 | )} 181 | 182 | } 183 | 184 | if ( typeof _kHMR === 'undefined' ) { // only load once, not when doing hot reloading 185 | global.app = ; 186 | // required for hot reloading 187 | window._kreactapprender = ReactDOM.render(global.app,document.getElementById("root")); 188 | } 189 | -------------------------------------------------------------------------------- /src/main/web/client/materialui/materialplay.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Step, 4 | Stepper, 5 | StepLabel, 6 | } from 'material-ui/Stepper'; 7 | import RaisedButton from 'material-ui/RaisedButton'; 8 | import FlatButton from 'material-ui/FlatButton'; 9 | import Badge from 'material-ui/Badge'; 10 | import IconButton from 'material-ui/IconButton'; 11 | import NotificationsIcon from 'material-ui/svg-icons/social/notifications'; 12 | import Chip from 'material-ui/Chip'; 13 | import TextField from 'material-ui/TextField'; 14 | 15 | const StepOpt = [ 16 | 'Select campaign settings...', 17 | 'What is an super ad group anyways?', 18 | 'This is the bit I really care about!', 19 | ]; 20 | /** 21 | * Horizontal steppers are ideal when the contents of one step depend on an earlier step. 22 | * Avoid using long step names in horizontal steppers. 23 | * 24 | * Linear steppers require users to complete one step in order to move on to the next. 25 | */ 26 | class HorizontalLinearStepper extends React.Component { 27 | 28 | constructor(props) { 29 | super(props); 30 | this.state = { 31 | finished: false, 32 | stepIndex: 0, 33 | }; 34 | } 35 | 36 | handleNext() { 37 | const {stepIndex} = this.state; 38 | this.setState({ 39 | stepIndex: stepIndex + 1, 40 | finished: stepIndex >= 2, 41 | }); 42 | }; 43 | 44 | handlePrev() { 45 | const {stepIndex} = this.state; 46 | if (stepIndex > 0) { 47 | this.setState({stepIndex: stepIndex - 1}); 48 | } 49 | }; 50 | 51 | getStepContent(stepIndex) { 52 | switch (stepIndex) { 53 | case 0: 54 | case 1: 55 | case 2: 56 | return StepOpt[stepIndex]; 57 | default: 58 | return 'You\'re a long way from home sonny jim!'; 59 | } 60 | } 61 | 62 | render() { 63 | const {finished, stepIndex} = this.state; 64 | const contentStyle = {margin: '0 16px'}; 65 | 66 | return ( 67 |
68 | 69 | 70 | {StepOpt[0]} 71 | 72 | 73 | {StepOpt[1]} 74 | 75 | 76 | {StepOpt[2]} 77 | 78 | 79 |
80 | {finished ? ( 81 |

82 | { 85 | event.preventDefault(); 86 | this.setState({stepIndex: 0, finished: false}); 87 | }} 88 | > 89 | Click here 90 | to reset the example. 91 |

92 | ) : ( 93 |
94 |

{this.getStepContent(stepIndex)}

95 |
96 | this.handlePrev(e) } 100 | style={{marginRight: 12}} 101 | /> 102 | this.handleNext(e)} 106 | /> 107 |
108 |
109 | )} 110 |
111 |
112 | ); 113 | } 114 | } 115 | 116 | const badges = { 117 | one: 5, 118 | two: 10 119 | }; 120 | 121 | const BadgeExampleSimple = () => ( 122 |
123 | 127 | 128 | 129 | 134 | 135 | 136 | 137 | 138 |
139 | ); 140 | 141 | class ChipExampleArray extends React.Component { 142 | 143 | constructor(props) { 144 | super(props); 145 | this.state = {chipData: [ 146 | {key: 0, label: 'Angular'}, 147 | {key: 1, label: 'JQuery'}, 148 | {key: 2, label: 'Polymer'}, 149 | {key: 3, label: 'ReactJS'}, 150 | ]}; 151 | this.styles = { 152 | chip: { 153 | margin: 4, 154 | }, 155 | wrapper: { 156 | display: 'flex', 157 | flexWrap: 'wrap', 158 | }, 159 | }; 160 | } 161 | 162 | handleRequestDelete(key) { 163 | if (key === 3) { 164 | alert('Why would you want to delete React?! :)'); 165 | return; 166 | } 167 | 168 | this.chipData = this.state.chipData; 169 | const chipToDelete = this.chipData.map((chip) => chip.key).indexOf(key); 170 | this.chipData.splice(chipToDelete, 1); 171 | this.setState({chipData: this.chipData}); 172 | }; 173 | 174 | renderChip(data) { 175 | return ( 176 | this.handleRequestDelete(data.key)} 179 | style={this.styles.chip} 180 | > 181 | {data.label} 182 | 183 | ); 184 | } 185 | 186 | render() { 187 | return ( 188 |
189 | {this.state.chipData.map(this.renderChip, this)} 190 |
191 | ); 192 | } 193 | } 194 | 195 | const TextFieldExampleSimple = () => ( 196 |
197 |
200 |
201 |
204 |
208 |
212 |
216 |
221 |
226 |
232 |
238 | 242 |
243 | ); 244 | 245 | let Dummy = function() { return "Hello anononymous"} 246 | function Dummy1() { return "Hello named function";} 247 | const DummyLambda = () => { return "Hello lambda function"; }; 248 | 249 | class MaterialPlay extends React.Component { 250 | 251 | render() { 252 | return ( 253 |
254 |

Material UI

255 |

{"Dummy:'"+Dummy()+"' Dummy1:'"+Dummy1()+"' DummyLambda:'"+DummyLambda()+"'"}

256 | 257 |
258 | 259 |
260 |
261 | 262 | 263 |
264 |
265 | ); 266 | } 267 | } 268 | 269 | export default MaterialPlay; -------------------------------------------------------------------------------- /src/main/web/client/static.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Static = () => { 4 | return ( 5 |
6 |

Example showcasing ..

7 |
    8 |
  • kontraktors .jsx transpiler
  • 9 |
  • kontraktors npm clone ("JNPM")
  • 10 |
  • no nodejs required
  • 11 |
  • Session handling
  • 12 |
  • Session timeout handling
  • 13 |
  • Session resurrection handling (SPA client was away/offline and connects back)
  • 14 |
  • Dynamic connection type (http adaptive long poll or websockets) without code change
  • 15 |
  • Set DEVMODE in ReactMaterialUITestApp to false to get production-level-bundling+optimization
  • 16 |
17 |
18 | ) 19 | }; 20 | 21 | export default Static; -------------------------------------------------------------------------------- /src/main/web/client/util.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | export class HCenter extends Component { 4 | 5 | render() { 6 | const style = { 7 | alignContent: 'center', 8 | alignItems: 'center', 9 | boxSizing: 'border-box', 10 | display: 'flex', 11 | flexDirection: 'row', 12 | flexWrap: 'nowrap', 13 | justifyContent: 'center', 14 | ...this.props.style 15 | }; 16 | return (
{this.props.children}
) 17 | } 18 | 19 | } 20 | 21 | export class Fader extends Component { 22 | constructor(p) { 23 | super(p); 24 | this.state={op:0}; 25 | } 26 | 27 | componentDidMount() { 28 | setTimeout( () => this.setState({op:1}), 100) 29 | } 30 | render() { 31 | const style = { 32 | opacity: this.state.op, 33 | transition: "opacity .5s" 34 | }; 35 | return (
{this.props.children}
) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/web/jnpm.kson: -------------------------------------------------------------------------------- 1 | JNPMConfig { 2 | versionMap: 3 | { 4 | # avoid getting beta 5 | react : "~16.6.0" 6 | react-dom : "~16.6.0" 7 | material-ui: "~0.20.0" 8 | } 9 | repo: "https://registry.npmjs.org/" 10 | ignoredProdRequires: ['./cjs/react.development.js' './cjs/react-dom.development.js'] 11 | ignoredDevRequires: ['./cjs/react.production.min.js' './cjs/react-dom.production.min.js'] 12 | } -------------------------------------------------------------------------------- /src/main/web/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intrinsicreact", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/runtime": { 8 | "version": "7.0.0", 9 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", 10 | "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", 11 | "requires": { 12 | "regenerator-runtime": "0.12.1" 13 | }, 14 | "dependencies": { 15 | "regenerator-runtime": { 16 | "version": "0.12.1", 17 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", 18 | "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" 19 | } 20 | } 21 | }, 22 | "asap": { 23 | "version": "2.0.6", 24 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 25 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 26 | }, 27 | "babel-runtime": { 28 | "version": "6.26.0", 29 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 30 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 31 | "requires": { 32 | "core-js": "2.5.7", 33 | "regenerator-runtime": "0.11.1" 34 | } 35 | }, 36 | "bowser": { 37 | "version": "1.9.4", 38 | "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", 39 | "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==" 40 | }, 41 | "chain-function": { 42 | "version": "1.0.1", 43 | "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz", 44 | "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==" 45 | }, 46 | "change-emitter": { 47 | "version": "0.1.6", 48 | "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", 49 | "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" 50 | }, 51 | "core-js": { 52 | "version": "2.5.7", 53 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", 54 | "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" 55 | }, 56 | "css-in-js-utils": { 57 | "version": "2.0.1", 58 | "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", 59 | "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", 60 | "requires": { 61 | "hyphenate-style-name": "1.0.2", 62 | "isobject": "3.0.1" 63 | } 64 | }, 65 | "dom-helpers": { 66 | "version": "3.4.0", 67 | "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", 68 | "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", 69 | "requires": { 70 | "@babel/runtime": "7.1.2" 71 | }, 72 | "dependencies": { 73 | "@babel/runtime": { 74 | "version": "7.1.2", 75 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.1.2.tgz", 76 | "integrity": "sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==", 77 | "requires": { 78 | "regenerator-runtime": "0.12.1" 79 | } 80 | }, 81 | "regenerator-runtime": { 82 | "version": "0.12.1", 83 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", 84 | "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" 85 | } 86 | } 87 | }, 88 | "encoding": { 89 | "version": "0.1.12", 90 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", 91 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", 92 | "requires": { 93 | "iconv-lite": "0.4.24" 94 | } 95 | }, 96 | "fbjs": { 97 | "version": "0.8.17", 98 | "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", 99 | "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", 100 | "requires": { 101 | "core-js": "1.2.7", 102 | "isomorphic-fetch": "2.2.1", 103 | "loose-envify": "1.4.0", 104 | "object-assign": "4.1.1", 105 | "promise": "7.3.1", 106 | "setimmediate": "1.0.5", 107 | "ua-parser-js": "0.7.19" 108 | }, 109 | "dependencies": { 110 | "core-js": { 111 | "version": "1.2.7", 112 | "resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", 113 | "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" 114 | } 115 | } 116 | }, 117 | "hoist-non-react-statics": { 118 | "version": "2.5.5", 119 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", 120 | "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" 121 | }, 122 | "hyphenate-style-name": { 123 | "version": "1.0.2", 124 | "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", 125 | "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" 126 | }, 127 | "iconv-lite": { 128 | "version": "0.4.24", 129 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 130 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 131 | "requires": { 132 | "safer-buffer": "2.1.2" 133 | } 134 | }, 135 | "inline-style-prefixer": { 136 | "version": "3.0.8", 137 | "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz", 138 | "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=", 139 | "requires": { 140 | "bowser": "1.9.4", 141 | "css-in-js-utils": "2.0.1" 142 | } 143 | }, 144 | "is-stream": { 145 | "version": "1.1.0", 146 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 147 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 148 | }, 149 | "isobject": { 150 | "version": "3.0.1", 151 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 152 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 153 | }, 154 | "isomorphic-fetch": { 155 | "version": "2.2.1", 156 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", 157 | "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", 158 | "requires": { 159 | "node-fetch": "1.7.3", 160 | "whatwg-fetch": "3.0.0" 161 | } 162 | }, 163 | "js-tokens": { 164 | "version": "4.0.0", 165 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 166 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 167 | }, 168 | "keycode": { 169 | "version": "2.2.0", 170 | "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", 171 | "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=" 172 | }, 173 | "lodash.merge": { 174 | "version": "4.6.1", 175 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", 176 | "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" 177 | }, 178 | "lodash.throttle": { 179 | "version": "4.1.1", 180 | "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", 181 | "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" 182 | }, 183 | "loose-envify": { 184 | "version": "1.4.0", 185 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 186 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 187 | "requires": { 188 | "js-tokens": "4.0.0" 189 | } 190 | }, 191 | "material-ui": { 192 | "version": "0.20.2", 193 | "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.20.2.tgz", 194 | "integrity": "sha512-VeqgQkdvtK193w+FFvXDEwlVxI4rWk83eWbpYLeOIHDPWr3rbB9B075JRnJt/8IsI2X8q5Aia5W3+7m4KkleDg==", 195 | "requires": { 196 | "babel-runtime": "6.26.0", 197 | "inline-style-prefixer": "3.0.8", 198 | "keycode": "2.2.0", 199 | "lodash.merge": "4.6.1", 200 | "lodash.throttle": "4.1.1", 201 | "prop-types": "15.6.2", 202 | "react-event-listener": "0.6.4", 203 | "react-transition-group": "1.2.1", 204 | "recompose": "0.26.0", 205 | "simple-assign": "0.1.0", 206 | "warning": "3.0.0" 207 | } 208 | }, 209 | "node-fetch": { 210 | "version": "1.7.3", 211 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", 212 | "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", 213 | "requires": { 214 | "encoding": "0.1.12", 215 | "is-stream": "1.1.0" 216 | } 217 | }, 218 | "object-assign": { 219 | "version": "4.1.1", 220 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 221 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 222 | }, 223 | "promise": { 224 | "version": "7.3.1", 225 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", 226 | "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", 227 | "requires": { 228 | "asap": "2.0.6" 229 | } 230 | }, 231 | "prop-types": { 232 | "version": "15.6.2", 233 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", 234 | "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", 235 | "requires": { 236 | "loose-envify": "1.4.0", 237 | "object-assign": "4.1.1" 238 | } 239 | }, 240 | "react": { 241 | "version": "16.6.0", 242 | "resolved": "https://registry.npmjs.org/react/-/react-16.6.0.tgz", 243 | "integrity": "sha512-zJPnx/jKtuOEXCbQ9BKaxDMxR0001/hzxXwYxG8septeyYGfsgAei6NgfbVgOhbY1WOP2o3VPs/E9HaN+9hV3Q==", 244 | "requires": { 245 | "loose-envify": "1.4.0", 246 | "object-assign": "4.1.1", 247 | "prop-types": "15.6.2", 248 | "scheduler": "0.10.0" 249 | } 250 | }, 251 | "react-dom": { 252 | "version": "16.6.0", 253 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.0.tgz", 254 | "integrity": "sha512-Stm2D9dXEUUAQdvpvhvFj/DEXwC2PAL/RwEMhoN4dvvD2ikTlJegEXf97xryg88VIAU22ZAP7n842l+9BTz6+w==", 255 | "requires": { 256 | "loose-envify": "1.4.0", 257 | "object-assign": "4.1.1", 258 | "prop-types": "15.6.2", 259 | "scheduler": "0.10.0" 260 | } 261 | }, 262 | "react-event-listener": { 263 | "version": "0.6.4", 264 | "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.4.tgz", 265 | "integrity": "sha512-t7VSjIuUFmN+GeyKb+wm025YLeojVB85kJL6sSs0wEBJddfmKBEQz+CNBZ2zBLKVWkPy/fZXM6U5yvojjYBVYQ==", 266 | "requires": { 267 | "@babel/runtime": "7.0.0", 268 | "prop-types": "15.6.2", 269 | "warning": "4.0.2" 270 | }, 271 | "dependencies": { 272 | "warning": { 273 | "version": "4.0.2", 274 | "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", 275 | "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", 276 | "requires": { 277 | "loose-envify": "1.4.0" 278 | } 279 | } 280 | } 281 | }, 282 | "react-transition-group": { 283 | "version": "1.2.1", 284 | "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz", 285 | "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==", 286 | "requires": { 287 | "chain-function": "1.0.1", 288 | "dom-helpers": "3.4.0", 289 | "loose-envify": "1.4.0", 290 | "prop-types": "15.6.2", 291 | "warning": "3.0.0" 292 | } 293 | }, 294 | "recompose": { 295 | "version": "0.26.0", 296 | "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", 297 | "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", 298 | "requires": { 299 | "change-emitter": "0.1.6", 300 | "fbjs": "0.8.17", 301 | "hoist-non-react-statics": "2.5.5", 302 | "symbol-observable": "1.2.0" 303 | } 304 | }, 305 | "regenerator-runtime": { 306 | "version": "0.11.1", 307 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 308 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" 309 | }, 310 | "safer-buffer": { 311 | "version": "2.1.2", 312 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 313 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 314 | }, 315 | "scheduler": { 316 | "version": "0.10.0", 317 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.10.0.tgz", 318 | "integrity": "sha512-+TSTVTCBAA3h8Anei3haDc1IRwMeDmtI/y/o3iBe3Mjl2vwYF9DtPDt929HyRmV/e7au7CLu8sc4C4W0VOs29w==", 319 | "requires": { 320 | "loose-envify": "1.4.0", 321 | "object-assign": "4.1.1" 322 | } 323 | }, 324 | "setimmediate": { 325 | "version": "1.0.5", 326 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", 327 | "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" 328 | }, 329 | "simple-assign": { 330 | "version": "0.1.0", 331 | "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz", 332 | "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o=" 333 | }, 334 | "symbol-observable": { 335 | "version": "1.2.0", 336 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", 337 | "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" 338 | }, 339 | "ua-parser-js": { 340 | "version": "0.7.19", 341 | "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", 342 | "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" 343 | }, 344 | "warning": { 345 | "version": "3.0.0", 346 | "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", 347 | "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", 348 | "requires": { 349 | "loose-envify": "1.4.0" 350 | } 351 | }, 352 | "whatwg-fetch": { 353 | "version": "3.0.0", 354 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", 355 | "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" 356 | } 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /src/main/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intrinsicreact", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "~16.6.0", 13 | "react-dom": "~16.6.0", 14 | "material-ui": "~0.20.0" 15 | } 16 | } 17 | --------------------------------------------------------------------------------