├── .circleci └── config.yml ├── .gitignore ├── .sbtopts ├── .scalafmt.conf ├── LICENSE ├── angular ├── README.md ├── src │ └── main │ │ ├── js │ │ ├── index.html │ │ └── styling.css │ │ └── scala │ │ └── demo │ │ ├── AppComponent.scala │ │ ├── AppModule.scala │ │ ├── AppRoutingModule.scala │ │ ├── Main.scala │ │ ├── heroeditor │ │ ├── DashboardComponent.scala │ │ ├── Hero.scala │ │ ├── HeroDetailComponent.scala │ │ ├── HeroService.scala │ │ ├── HeroesComponent.scala │ │ ├── MessageService.scala │ │ ├── MessagesComponent.scala │ │ └── MockHeroes.scala │ │ └── package.scala └── yarn.lock ├── build.sbt ├── chart ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── custom.webpack.config.js ├── cypress ├── cypress.json ├── cypress │ ├── plugins │ │ └── index.js │ ├── support │ │ ├── commands.js │ │ └── index.js │ └── videos │ │ └── main.js.mp4 ├── src │ └── main │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── d3 ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── docs ├── angular │ ├── angular-opt-bundle.js │ └── index.html ├── chart │ ├── chart-opt-bundle.js │ └── index.html ├── d3 │ ├── d3-opt-bundle.js │ └── index.html ├── google-maps │ ├── google-maps-opt-bundle.js │ └── index.html ├── jquery │ ├── 05d14cb826bafefd5b46fa2e3be5c952.png │ ├── 0f8012f9214f21e71d2e09dd59620cff.png │ ├── 3676d240bfc41073d4b9a3886dbb2c2d.png │ ├── 3e7b25be8db5575f37ef732e60ea6eb4.png │ ├── 546138d93ba6d3da76e1cefdd3493b25.png │ ├── 5c18c7c3b45a9e304e1da3da3f6c517f.png │ ├── 5e67153340a01757d1ee073aa6fdbdd8.png │ ├── 6451ae9a01ac65bd0fd7eb118e7770f1.png │ ├── 6616f7b7e32f5346b03964406a933d49.png │ ├── 6b0513b86cddf85f3764609bbfedfd79.png │ ├── 7ebbd63f8c28bf02ab557026bb078116.png │ ├── aabe52d4b720fc416d5fa1bf79849f8c.png │ ├── b159d479704de144f3bde7eb8c232ce5.png │ ├── b5c094c2859bb622056ba7a883112aec.png │ ├── index.html │ └── jquery-opt-bundle.js ├── leaflet │ ├── index.html │ └── leaflet-opt-bundle.js ├── onsenui │ ├── index.html │ └── onsenui-opt-bundle.js ├── p5 │ ├── index.html │ └── p5-opt-bundle.js ├── phaser │ ├── 4c749ba5f2242b64e37fa7439ece8c98.png │ ├── ff69a6fb51aef4d4950ff9398136e979.jsn │ ├── index.html │ └── phaser-opt-bundle.js ├── pixi │ ├── 27905594c5fb6a699e83e2a33ac4594f.jpg │ ├── 3cd1bac088de1f0141a2d2b460f5e604.jpeg │ ├── 4570873d87d3a0f6e5ac1cb8fe25e0d3.mp4 │ ├── 4570873d87d3a0f6e5ac1cb8fe25e0d3.png │ ├── 671dfaa933552f588d943ca39375807e.png │ ├── 70f6dd03a1f4a89b372199a507d13413.mp4 │ ├── 81caa7d77cd7fe45112c79d65902fe6f.jpg │ ├── 8bfb7c2cc94d5e2fe3e9eb8766c2143c.png │ ├── 98caa6066cf537444105787d26f45c13.jpg │ ├── 9cc4b590bde4d70f18cb9ba64776ae2b.png │ ├── ae108d533b303b523de5393a7e0f4b0e.jpeg │ ├── c79a9d4407a11aa2611443c9d8ea7d70.png │ ├── e6485b0aaa9ff710c4454df3a144efb1.png │ ├── index.html │ └── pixi-opt-bundle.js ├── reveal │ ├── 10560fb6663a782589a3f7f8926bae05.eot │ ├── 18847feae8b114c355f01927e6b734bb.eot │ ├── 1cb8e94f1185f1131a0c895165998f2b.woff │ ├── 2da39ecf9246383937da11b44b7bd9b4.ttf │ ├── 6b058fc2634b01d837c3432316c3141f.woff │ ├── 80fbb267a50233879359bdf53b671515.eot │ ├── 8256cfd7e4017a7690814879409212cd.ttf │ ├── c2491edf9e2b71393eeb226d6f3198fc.eot │ ├── c7e698a4d0956f4a939f42a05685bbf5.ttf │ ├── e74f0128884561828ce8c9cf5c284ab8.woff │ ├── e7acc589bb558fe58936a853f570193c.woff │ ├── f3565095e6c9158140444970f5a2c5ed.ttf │ ├── index.html │ └── reveal-opt-bundle.js ├── three │ ├── ebf4afeb87874d0a1487e7ea687fb529.glb │ ├── index.html │ └── three-opt-bundle.js └── vue │ ├── index.html │ └── vue-opt-bundle.js ├── electron ├── package.json ├── src │ └── main │ │ ├── resources │ │ ├── index.html │ │ └── package.json │ │ └── scala │ │ └── mainprocess │ │ └── MainProcess.scala └── yarn.lock ├── google-maps ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── jquery ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── leaflet ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── lodash ├── src │ └── main │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── node-express ├── src │ └── main │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── onsenui ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── p5 ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── phaser ├── src │ └── main │ │ ├── js │ │ ├── gems.jsn │ │ ├── gems.png │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── pixi ├── src │ └── main │ │ ├── js │ │ ├── a11y-light.css │ │ ├── assets │ │ │ ├── bg_grass.jpg │ │ │ ├── bg_scene_rotate.jpg │ │ │ ├── bunny.png │ │ │ ├── eggHead.png │ │ │ ├── p2.jpeg │ │ │ ├── pixi-filters │ │ │ │ ├── displacement_map_repeat.jpg │ │ │ │ └── flag.png │ │ │ ├── star.png │ │ │ ├── trail.png │ │ │ └── video.mp4 │ │ ├── index.html │ │ └── styles.css │ │ └── scala │ │ └── demo │ │ ├── assets.scala │ │ ├── demosadvanced │ │ ├── MouseTrail.scala │ │ └── StarWarp.scala │ │ ├── demosbasic │ │ ├── Basics.scala │ │ ├── ContainerPivot.scala │ │ ├── Containers.scala │ │ └── Tinting.scala │ │ ├── filtersbasic │ │ └── DisplacementMapFlag.scala │ │ ├── graphics │ │ └── Simple.scala │ │ ├── interaction │ │ └── Click.scala │ │ ├── meshandshaders │ │ └── Uniform.scala │ │ ├── monkeypatching │ │ └── PIXIPatching.scala │ │ ├── pixi │ │ ├── ExampleSelector.scala │ │ └── PIXIExample.scala │ │ ├── pluginfilters │ │ └── Outline.scala │ │ └── sprite │ │ ├── TilingSpriteExample.scala │ │ └── VideoExample.scala └── yarn.lock ├── project ├── build.properties ├── plugins.sbt └── scalafmt.sbt ├── readme.md ├── reveal ├── src │ └── main │ │ ├── js │ │ └── index.html │ │ └── scala │ │ └── demo │ │ ├── Demo.scala │ │ ├── MyTalk.scala │ │ └── PresentationUtil.scala └── yarn.lock ├── three ├── src │ └── main │ │ ├── js │ │ ├── Horse.glb │ │ └── index.html │ │ └── scala │ │ └── demo.scala └── yarn.lock ├── typescript ├── src │ └── main │ │ ├── resources │ │ ├── bad.js │ │ ├── bad.ts │ │ ├── good.js │ │ └── good.ts │ │ └── scala │ │ └── demo.scala └── yarn.lock └── vue ├── src └── main │ ├── js │ └── index.html │ └── scala │ └── demo.scala └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Scala CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/sample-config/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/openjdk:8-jdk-node 11 | # Specify service dependencies here if necessary 12 | # CircleCI maintains a library of pre-built images 13 | # documented at https://circleci.com/docs/2.0/circleci-images/ 14 | # - image: circleci/postgres:9.4 15 | 16 | working_directory: ~/repo 17 | 18 | environment: 19 | # Customize the JVM maximum heap limit 20 | JVM_OPTS: -Xmx3200m 21 | TERM: dumb 22 | 23 | steps: 24 | - checkout 25 | 26 | # Download and cache dependencies 27 | - restore_cache: 28 | keys: 29 | - v1-dependencies-4-{{ checksum "build.sbt" }} 30 | # fallback to using the latest cache if no exact match is found 31 | - v1-dependencies-4- 32 | 33 | # ideally we would bundle and run things, but the diversity of the demos makes it a bit difficult. 34 | # might revisit this later 35 | - run: cat /dev/null | sbt stPublishCache compile 36 | 37 | - save_cache: 38 | paths: 39 | - ~/.sbt 40 | - ~/.ivy2/cache 41 | - ~/.ivy2/local 42 | - ~/.cache/scalablytyped 43 | key: v1-dependencies-4-{{ checksum "build.sbt" }} 44 | 45 | # # run scalafmt! 46 | # - run: cat /dev/null | sbt scalafmtCheck 47 | 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | .bloop/ 4 | node_modules/ 5 | .gradle/ 6 | build/ 7 | local.properties 8 | .expo/ 9 | .metals 10 | project/metals.sbt 11 | -------------------------------------------------------------------------------- /.sbtopts: -------------------------------------------------------------------------------- 1 | -J-XX:+UseG1GC 2 | -J-Xmx4g 3 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version=3.0.0-RC5 2 | runner.dialect = scala3 3 | style = default 4 | danglingParentheses.preset = true 5 | align { 6 | tokens.add = [":", "="] 7 | } 8 | maxColumn = 120 9 | rewrite { 10 | rules = [SortImports, RedundantBraces, RedundantParens, PreferCurlyFors] 11 | scala3 = { 12 | convertToNewSyntax = true, 13 | removeOptionalBraces: yes, 14 | insertEndMarkerMinLines = 10 15 | } 16 | } 17 | project.excludeFilters = [ 18 | node_modules, 19 | target 20 | ] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ScalablyTyped 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /angular/README.md: -------------------------------------------------------------------------------- 1 | # Angular Scala POC 2 | 3 | Use [Angular](https://angular.io/) 7 with [Scala](https://www.scala-lang.org/) and [Scala.js](https://www.scala-js.org/). 4 | 5 | The beginning of the "Tour of Heroes" tutorial is implemented. 6 | 7 | ## TypeScript decorators 8 | 9 | Scala.js compiles to pure JavaScript, and not TypeScript. Unfortunately, decorators do not exist in JS and we have to 10 | translate them. The way it's supposed to be done is explained 11 | [here](https://v2.angular.io/docs/ts/latest/cookbook/ts-to-js.html). 12 | 13 | In Scala.js, that translate to the decorated class having a companion object, that has a val `annotations` that is a 14 | `js.Array` that contains a Component, or a NgModule... 15 | 16 | ## Scala.js Angular specificities 17 | 18 | ### Decorators 19 | 20 | [This page](https://v2.angular.io/docs/ts/latest/cookbook/ts-to-js.html) explains how TypeScript decorators have to be 21 | translated into JavaScript. Essentially, for 22 | ```typescript 23 | @SomeDecorator({ ... }) 24 | class Foo {} 25 | ``` 26 | there has to be a JS array `annotations` on the object `Foo`. 27 | In Scala, this will be translated by setting a value 28 | `annotations: js.Array[Decorator]` to the companion object, and annotate it by `@JSExportStatic`. 29 | 30 | ### Injectables 31 | 32 | Injectables must be 33 | 34 | - set as providers within the NgModule 35 | - have their companion object have an `annotations` js-exported static field, which is a `js.Array[Injectable]` 36 | - be specified in the static js-exported `parameters` of the companion object in which they must be injected 37 | 38 | ### External templates and style sheets 39 | 40 | For HTML templates and CSS style sheets that are associated to a component via its decorator, the specified path has to 41 | be relative to the `index.html` file. 42 | 43 | ## Things to do 44 | 45 | - Find better way to handle the decorators 46 | -------------------------------------------------------------------------------- /angular/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Angular with Scala.js 7 | 8 | 9 |

Sorry, angular demo is currently broken

10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /angular/src/main/js/styling.css: -------------------------------------------------------------------------------- 1 | /* Master Styles */ 2 | h1 { 3 | color: #369; 4 | font-family: Arial, Helvetica, sans-serif; 5 | font-size: 250%; 6 | } 7 | h2, h3 { 8 | color: #444; 9 | font-family: Arial, Helvetica, sans-serif; 10 | font-weight: lighter; 11 | } 12 | body { 13 | margin: 2em; 14 | } 15 | body, input[text], button { 16 | color: #888; 17 | font-family: Cambria, Georgia; 18 | } 19 | a { 20 | cursor: pointer; 21 | cursor: hand; 22 | } 23 | button { 24 | font-family: Arial; 25 | background-color: #eee; 26 | border: none; 27 | padding: 5px 10px; 28 | border-radius: 4px; 29 | cursor: pointer; 30 | cursor: hand; 31 | } 32 | button:hover { 33 | background-color: #cfd8dc; 34 | } 35 | button:disabled { 36 | background-color: #eee; 37 | color: #aaa; 38 | cursor: auto; 39 | } 40 | 41 | /* Navigation link styles */ 42 | nav a { 43 | padding: 5px 10px; 44 | text-decoration: none; 45 | margin-right: 10px; 46 | margin-top: 10px; 47 | display: inline-block; 48 | background-color: #eee; 49 | border-radius: 4px; 50 | } 51 | nav a:visited, a:link { 52 | color: #607D8B; 53 | } 54 | nav a:hover { 55 | color: #039be5; 56 | background-color: #CFD8DC; 57 | } 58 | nav a.active { 59 | color: #039be5; 60 | } 61 | 62 | /* everywhere else */ 63 | * { 64 | font-family: Arial, Helvetica, sans-serif; 65 | } 66 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/AppComponent.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import typings.angularCore.mod.{Component, ComponentCls, OnInit} 4 | 5 | import scala.scalajs.js 6 | import scala.scalajs.js.annotation.JSExportStatic 7 | 8 | /** An example of a component. The @Component decorator is taken care of in the companion object, under annotations. 9 | * 10 | * AppComponent extends from [[OnInit]] so that it does stuff when initiated. 11 | */ 12 | final class AppComponent extends OnInit: 13 | val title: String = "Angular with Scala.js" 14 | 15 | def subtitle(): String = "This is a subtitle" 16 | 17 | override def ngOnInit(): Unit = println("AppComponent") 18 | end AppComponent 19 | 20 | object AppComponent: 21 | 22 | /** The @Component decorator for AppComponent. 23 | */ 24 | @JSExportStatic 25 | val annotations = js.Array( 26 | new ComponentCls( 27 | new Component {} 28 | .setSelector("app-root") 29 | .setTemplate(""" 30 |

{{ title }}

31 | 35 | 36 | 37 | 38 | """).setStylesVarargs(""" 39 | h1 { 40 | font-size: 1.2em; 41 | color: #999; 42 | margin-bottom: 0; 43 | } 44 | h2 { 45 | font-size: 2em; 46 | margin-top: 0; 47 | padding-top: 0; 48 | } 49 | nav a { 50 | padding: 5px 10px; 51 | text-decoration: none; 52 | margin-top: 10px; 53 | display: inline-block; 54 | background-color: #eee; 55 | border-radius: 4px; 56 | } 57 | nav a:visited, a:link { 58 | color: #607d8b; 59 | } 60 | nav a:hover { 61 | color: #039be5; 62 | background-color: #cfd8dc; 63 | } 64 | nav a.active { 65 | color: #039be5; 66 | } 67 | """) 68 | ) 69 | ) 70 | end AppComponent 71 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/AppModule.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import demo.heroeditor.* 4 | import typings.angularCore.mod.{NgModule, NgModuleCls} 5 | import typings.angularForms.mod.FormsModule 6 | import typings.angularPlatformBrowser.mod.BrowserModule 7 | 8 | import scala.scalajs.js 9 | import scala.scalajs.js.annotation.JSExportStatic 10 | 11 | /** The @NgModule Decorator is implemented in the companion object, under annotations static field. 12 | */ 13 | final class AppModule extends js.Object 14 | 15 | object AppModule: 16 | 17 | @JSExportStatic 18 | val annotations = js.Array( 19 | new NgModuleCls( 20 | new NgModule {} 21 | .setImportsVarargs( 22 | typeOf[BrowserModule], 23 | typeOf[FormsModule], 24 | typeOf[AppRoutingModule] 25 | ) 26 | .setDeclarationsVarargs( 27 | typeOf[AppComponent], 28 | typeOf[HeroesComponent], 29 | typeOf[HeroDetailComponent], 30 | typeOf[MessagesComponent], 31 | typeOf[DashboardComponent] 32 | ) 33 | .setBootstrapVarargs(typeOf[AppComponent]) 34 | .setProvidersVarargs(typeOf[HeroService], typeOf[MessageService]) 35 | ) 36 | ) 37 | end AppModule 38 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/AppRoutingModule.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import typings.angularCore.mod.{NgModule, NgModuleCls} 4 | import typings.angularRouter.mod.{Route, RouterModule, Routes} 5 | 6 | import scala.scalajs.js 7 | import scala.scalajs.js.annotation.JSExportStatic 8 | 9 | final class AppRoutingModule extends js.Object 10 | 11 | object AppRoutingModule: 12 | 13 | @JSExportStatic 14 | val annotations = 15 | js.Array( 16 | new NgModuleCls( 17 | new NgModule {} 18 | .setImportsVarargs(unspecify(RouterModule.forRoot(routes))) 19 | .setExports(js.Array(typeOf[RouterModule])) 20 | ) 21 | ) 22 | 23 | def routes: Routes = js.Array( 24 | Route().setPath("heroes").setComponent(typeOf[heroeditor.HeroesComponent]), 25 | Route().setPath("dashboard").setComponent(typeOf[heroeditor.DashboardComponent]), 26 | Route().setPath("").setRedirectTo("/dashboard").setPathMatch("full"), 27 | Route().setPath("detail/:id").setComponent(typeOf[heroeditor.HeroDetailComponent]) 28 | ) 29 | end AppRoutingModule 30 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/Main.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalajs.dom.{document, Event} 4 | import typings.angularCore.mod as Core 5 | import typings.angularPlatformBrowserDynamic.mod.platformBrowserDynamic 6 | 7 | import scala.scalajs.js 8 | import scala.scalajs.js.annotation.JSImport 9 | 10 | @js.native @JSImport("./styling.css", JSImport.Namespace) 11 | object Style extends js.Object 12 | 13 | /* rejoice this line, it took longer to write than the rest of the demo >: */ 14 | @js.native @JSImport("core-js/client/shim.min.js", JSImport.Namespace) 15 | object coreJsCustomRequire extends js.Object 16 | 17 | @main 18 | def main: Unit = 19 | /* touch to require */ 20 | coreJsCustomRequire 21 | typings.tslib.tslibRequire 22 | typings.zoneJs.zoneJsRequire 23 | Style 24 | 25 | /** Waiting the DOM to be loaded otherwise the CSS selectors won't exist. 26 | */ 27 | document.addEventListener[Event]( 28 | "DOMContentLoaded", 29 | (_: Event) => 30 | if !scala.scalajs.LinkingInfo.developmentMode then 31 | 32 | /** Syncing Angular production mode with Scala.js production mode. 33 | */ 34 | Core.enableProdMode() 35 | 36 | println("Charging App") 37 | platformBrowserDynamic().bootstrapModule(typeOf[AppModule]) 38 | ) 39 | end main 40 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/DashboardComponent.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | package heroeditor 3 | 4 | import typings.angularCore.mod.{Component, ComponentCls, OnInit} 5 | 6 | import scala.scalajs.js 7 | import scala.scalajs.js.annotation.JSExportStatic 8 | 9 | final class DashboardComponent(heroService: HeroService) extends OnInit: 10 | var heroes: js.Array[Hero] = MockHeroes.heroes.slice(1, 5) 11 | 12 | override def ngOnInit(): Unit = 13 | getHeroes() 14 | 15 | def getHeroes(): Unit = 16 | heroService.heroes.subscribe(hs => heroes = hs.slice(1, 5)) 17 | 18 | object DashboardComponent: 19 | 20 | @JSExportStatic 21 | val annotations = js.Array( 22 | new ComponentCls( 23 | new Component {} 24 | .setSelector("app-dashboard") 25 | .setTemplate( 26 | """ 27 | |

Top Heroes

28 | |
29 | | 31 | |
32 | |

{{hero.name}}

33 | |
34 | |
35 | |
36 | | 37 | """.stripMargin 38 | ) 39 | .setStylesVarargs(""" 40 | |/* DashboardComponent's private CSS styles */ 41 | |[class*='col-'] { 42 | | float: left; 43 | | padding-right: 20px; 44 | | padding-bottom: 20px; 45 | |} 46 | |[class*='col-']:last-of-type { 47 | | padding-right: 0; 48 | |} 49 | |a { 50 | | text-decoration: none; 51 | |} 52 | |*, *:after, *:before { 53 | | -webkit-box-sizing: border-box; 54 | | -moz-box-sizing: border-box; 55 | | box-sizing: border-box; 56 | |} 57 | |h3 { 58 | | text-align: center; margin-bottom: 0; 59 | |} 60 | |h4 { 61 | | position: relative; 62 | |} 63 | |.grid { 64 | | margin: 0; 65 | |} 66 | |.col-1-4 { 67 | | width: 25%; 68 | |} 69 | |.module { 70 | | padding: 20px; 71 | | text-align: center; 72 | | color: #eee; 73 | | max-height: 120px; 74 | | min-width: 120px; 75 | | background-color: #607d8b; 76 | | border-radius: 2px; 77 | |} 78 | |.module:hover { 79 | | background-color: #eee; 80 | | cursor: pointer; 81 | | color: #607d8b; 82 | |} 83 | |.grid-pad { 84 | | padding: 10px 0; 85 | |} 86 | |.grid-pad > [class*='col-']:last-of-type { 87 | | padding-right: 20px; 88 | |} 89 | |@media (max-width: 600px) { 90 | | .module { 91 | | font-size: 10px; 92 | | max-height: 75px; } 93 | |} 94 | |@media (max-width: 1024px) { 95 | | .grid { 96 | | margin: 0; 97 | | } 98 | | .module { 99 | | min-width: 60px; 100 | | } 101 | |} 102 | """.stripMargin) 103 | ) 104 | ) 105 | 106 | @JSExportStatic 107 | val parameters = js.Array(typeOf[HeroService]) 108 | end DashboardComponent 109 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/Hero.scala: -------------------------------------------------------------------------------- 1 | package demo.heroeditor 2 | 3 | import scala.scalajs.js 4 | 5 | class Hero(val id: Int, val name: String) extends js.Object 6 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/HeroDetailComponent.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | package heroeditor 3 | 4 | import typings.angularCommon.mod.Location 5 | import typings.angularCore.mod.{Component, ComponentCls, OnInit, Type} 6 | import typings.angularRouter.mod.ActivatedRoute 7 | 8 | import scala.scalajs.js 9 | import scala.scalajs.js.annotation.JSExportStatic 10 | 11 | final class HeroDetailComponent(route: ActivatedRoute, heroService: HeroService, location: Location) extends OnInit: 12 | 13 | var hero: js.UndefOr[Hero] = js.undefined 14 | 15 | def getHero(): Unit = 16 | asOption(route.snapshot.paramMap.get("id")) match 17 | case Some(id) if id.forall(_.isDigit) => 18 | hero = heroService.getHero(id.toInt) 19 | case _ => () 20 | 21 | override def ngOnInit(): Unit = 22 | getHero() 23 | println(hero) 24 | 25 | def goBack(): Unit = 26 | location.back() 27 | end HeroDetailComponent 28 | 29 | object HeroDetailComponent: 30 | @JSExportStatic 31 | val annotations = js.Array( 32 | new ComponentCls( 33 | new Component {} 34 | .setSelector("app-hero-detail") 35 | .setInputsVarargs("hero") 36 | .setTemplate(""" 37 |
38 |

{{hero.name | uppercase}} Details

39 |
id: {{hero.id}}
40 |
41 | 44 |
45 | 46 |
""") 47 | .setStylesVarargs(""" 48 | /* HeroDetailComponent's private CSS styles */ 49 | label { 50 | display: inline-block; 51 | width: 3em; 52 | margin: .5em 0; 53 | color: #607D8B; 54 | font-weight: bold; 55 | } 56 | input { 57 | height: 2em; 58 | font-size: 1em; 59 | padding-left: .4em; 60 | } 61 | button { 62 | margin-top: 20px; 63 | font-family: Arial; 64 | background-color: #eee; 65 | border: none; 66 | padding: 5px 10px; 67 | border-radius: 4px; 68 | cursor: pointer; cursor: hand; 69 | } 70 | button:hover { 71 | background-color: #cfd8dc; 72 | } 73 | button:disabled { 74 | background-color: #eee; 75 | color: #ccc; 76 | cursor: auto; 77 | } 78 | """) 79 | ) 80 | ) 81 | 82 | @JSExportStatic 83 | val parameters: js.Array[Type[?]] = js.Array( 84 | typeOf[ActivatedRoute], 85 | typeOf[HeroService], 86 | typeOf[Location] 87 | ) 88 | end HeroDetailComponent 89 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/HeroService.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | package heroeditor 3 | 4 | import typings.angularCore.mod.InjectableCls 5 | import typings.rxjs.internalObservableMod.Observable 6 | import typings.rxjs.mod as rxjs 7 | 8 | import scala.scalajs.js 9 | import scala.scalajs.js.JSConverters.* 10 | import scala.scalajs.js.annotation.JSExportStatic 11 | 12 | final class HeroService(messageService: MessageService) extends js.Object: 13 | def heroes: Observable[js.Array[Hero]] = 14 | messageService.add("HeroService: fetched heroes") 15 | rxjs.of(MockHeroes.heroes) 16 | 17 | def getHero(id: Int): js.UndefOr[Hero] = 18 | messageService.add(s"HeroService: fetched hero id=$id") 19 | MockHeroes.heroes.find(_.id == id).orUndefined 20 | 21 | object HeroService: 22 | @JSExportStatic 23 | val annotations = js.Array(new InjectableCls) 24 | 25 | @JSExportStatic 26 | val parameters = js.Array(typeOf[MessageService]) 27 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/HeroesComponent.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | package heroeditor 3 | 4 | import typings.angularCore.mod.{Component, ComponentCls, OnInit} 5 | 6 | import scala.scalajs.js 7 | import scala.scalajs.js.annotation.JSExportStatic 8 | 9 | final class HeroesComponent(heroService: HeroService) extends OnInit: 10 | 11 | var heroes: js.Array[Hero] = _ 12 | 13 | def getHeroes(): Unit = 14 | heroService.heroes.subscribe(hs => heroes = hs) 15 | heroes = MockHeroes.heroes 16 | 17 | override def ngOnInit(): Unit = 18 | println("Heroes Component") 19 | 20 | getHeroes() 21 | end HeroesComponent 22 | 23 | object HeroesComponent: 24 | @JSExportStatic 25 | val annotations = js.Array( 26 | new ComponentCls( 27 | new Component {} 28 | .setSelector("app-heroes") 29 | .setTemplate( 30 | """ 31 | | 32 | |

My Heroes

33 | | 34 | | 41 | """.stripMargin 42 | ) 43 | .setStylesVarargs( 44 | """ 45 | |/* HeroesComponent's private CSS styles */ 46 | |.selected { 47 | | background-color: #CFD8DC !important; 48 | | color: white; 49 | |} 50 | |.heroes { 51 | | margin: 0 0 2em 0; 52 | | list-style-type: none; 53 | | padding: 0; 54 | | width: 15em; 55 | |} 56 | |.heroes li { 57 | | cursor: pointer; 58 | | position: relative; 59 | | left: 0; 60 | | background-color: #EEE; 61 | | margin: .5em; 62 | | padding: .3em 0; 63 | | height: 1.6em; 64 | | border-radius: 4px; 65 | |} 66 | |.heroes li.selected:hover { 67 | | background-color: #BBD8DC !important; 68 | | color: white; 69 | |} 70 | |.heroes li:hover { 71 | | color: #607D8B; 72 | | background-color: #DDD; 73 | | left: .1em; 74 | |} 75 | |.heroes .text { 76 | | position: relative; 77 | | top: -3px; 78 | |} 79 | |.heroes .badge { 80 | | display: inline-block; 81 | | font-size: small; 82 | | color: white; 83 | | padding: 0.8em 0.7em 0 0.7em; 84 | | background-color: #607D8B; 85 | | line-height: 1em; 86 | | position: relative; 87 | | left: -1px; 88 | | top: -4px; 89 | | height: 1.8em; 90 | | min-width: 16px; 91 | | text-align: right; 92 | | margin-right: .8em; 93 | | border-radius: 4px 0 0 4px; 94 | |} 95 | """.stripMargin 96 | ) 97 | ) 98 | ) 99 | 100 | @JSExportStatic 101 | val parameters = js.Array(typeOf[HeroService]) 102 | end HeroesComponent 103 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/MessageService.scala: -------------------------------------------------------------------------------- 1 | package demo.heroeditor 2 | 3 | import typings.angularCore.mod.InjectableCls 4 | 5 | import scala.scalajs.js 6 | import scala.scalajs.js.annotation.JSExportStatic 7 | 8 | final class MessageService extends js.Object: 9 | val messages: js.Array[String] = js.Array() 10 | 11 | def add(message: String): Unit = 12 | messages.push(message) 13 | 14 | def clear(): Unit = 15 | messages.clear() 16 | 17 | object MessageService: 18 | @JSExportStatic 19 | val annotations = js.Array(new InjectableCls) 20 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/MessagesComponent.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | package heroeditor 3 | 4 | import typings.angularCore.mod.{Component, ComponentCls} 5 | 6 | import scala.scalajs.js 7 | import scala.scalajs.js.annotation.JSExportStatic 8 | 9 | final class MessagesComponent(val messageService: MessageService) extends js.Object: 10 | def ngOnInit(): Unit = () 11 | 12 | object MessagesComponent: 13 | @JSExportStatic 14 | val annotations = 15 | js.Array( 16 | new ComponentCls( 17 | new Component {} 18 | .setSelector("app-messages") 19 | .setTemplate(""" 20 |
21 | 22 |

Messages

23 | 25 |
{{message}}
26 | 27 |
""") 28 | .setStylesVarargs(""" 29 | h2 { 30 | color: red; 31 | font-family: Arial, Helvetica, sans-serif; 32 | font-weight: lighter; 33 | } 34 | body { 35 | margin: 2em; 36 | } 37 | body, input[text], button { 38 | color: crimson; 39 | font-family: Cambria, Georgia; 40 | } 41 | 42 | button.clear { 43 | font-family: Arial; 44 | background-color: #eee; 45 | border: none; 46 | padding: 5px 10px; 47 | border-radius: 4px; 48 | cursor: pointer; 49 | cursor: hand; 50 | } 51 | button:hover { 52 | background-color: #cfd8dc; 53 | } 54 | button:disabled { 55 | background-color: #eee; 56 | color: #aaa; 57 | cursor: auto; 58 | } 59 | button.clear { 60 | color: #888; 61 | margin-bottom: 12px; 62 | } 63 | """) 64 | ) 65 | ) 66 | 67 | @JSExportStatic 68 | val parameters = js.Array(typeOf[MessageService]) 69 | end MessagesComponent 70 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/heroeditor/MockHeroes.scala: -------------------------------------------------------------------------------- 1 | package demo.heroeditor 2 | 3 | import scala.scalajs.js 4 | 5 | object MockHeroes: 6 | 7 | private val names = js.Array( 8 | "Mr. Nice", 9 | "Narco", 10 | "Bombasto", 11 | "Celeritas", 12 | "Magneta", 13 | "RubberMan", 14 | "Dynama", 15 | "Dr IQ", 16 | "Magma", 17 | "Tornado" 18 | ) 19 | 20 | val heroes: js.Array[Hero] = 21 | names.zipWithIndex.map { case (n, index) => new Hero(index + 11, n) } 22 | end MockHeroes 23 | -------------------------------------------------------------------------------- /angular/src/main/scala/demo/package.scala: -------------------------------------------------------------------------------- 1 | import typings.angularCore.mod.Type 2 | 3 | import scala.language.higherKinds 4 | import scala.scalajs.js 5 | import scala.scalajs.js.| 6 | 7 | package object demo: 8 | 9 | /** Get the Type[T] of a Class, by calling js.constructorOf 10 | */ 11 | @inline def typeOf[T <: js.Any](implicit tag: js.ConstructorTag[T]): Type[Any] = 12 | tag.constructor.asInstanceOf[Type[Any]] 13 | 14 | /* pretend that the world is contravariant */ 15 | @inline def unspecify[M[_], T <: js.Object](mt: M[T]): M[js.Object] = 16 | mt.asInstanceOf[M[js.Object]] 17 | 18 | @inline def asOption[T](ot: T | Null): Option[T] = 19 | Option(ot.asInstanceOf[T]) 20 | end demo 21 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files 2 | import java.nio.file.StandardCopyOption.REPLACE_EXISTING 3 | 4 | import org.scalajs.jsenv.nodejs.NodeJSEnv 5 | 6 | import scala.sys.process.Process 7 | 8 | onLoad in Global := { 9 | println( 10 | """* 11 | |* Welcome to ScalablyTyped demos! 12 | |* 13 | |* For documentation see https://scalablytyped.org . 14 | |* 15 | |* Note that the first time you import/compile the projects it'll take a while for the dependencies to build 16 | |*""".stripMargin 17 | ) 18 | (onLoad in Global).value 19 | } 20 | 21 | Global / stRemoteCache := RemoteCache.S3Aws( 22 | bucket = "scalablytyped-demos", 23 | region = "eu-central-1", 24 | prefix = Some("st-cache") 25 | ) 26 | 27 | /** 28 | * Custom task to start demo with webpack-dev-server, use as `/start`. 29 | * Just `start` also works, and starts all frontend demos 30 | * 31 | * After that, the incantation is this to watch and compile on change: 32 | * `~/fastOptJS::webpack` 33 | */ 34 | lazy val start = TaskKey[Unit]("start") 35 | 36 | /** Say just `dist` or `/dist` to make a production bundle in 37 | * `docs` for github publishing 38 | */ 39 | lazy val dist = TaskKey[File]("dist") 40 | 41 | lazy val vue = 42 | project 43 | .enablePlugins(ScalablyTypedConverterPlugin) 44 | .configure(baseSettings, bundlerSettings, browserProject) 45 | .settings( 46 | Compile / npmDependencies ++= Seq("vue" -> "2.6.11"), 47 | useYarn := true, 48 | webpackDevServerPort := 8004 49 | ) 50 | 51 | lazy val three = 52 | project 53 | .enablePlugins(ScalablyTypedConverterPlugin) 54 | .configure(baseSettings, bundlerSettings, browserProject, withCssLoading) 55 | .settings( 56 | Compile / npmDependencies ++= Seq("three" -> "0.112.1"), 57 | stUseScalaJsDom := false, 58 | webpackDevServerPort := 8005, 59 | useYarn := true 60 | ) 61 | 62 | lazy val d3 = project 63 | .enablePlugins(ScalablyTypedConverterPlugin) 64 | .configure(baseSettings, bundlerSettings, browserProject) 65 | .settings( 66 | Compile / npmDependencies ++= Seq( 67 | "d3" -> "5.15", 68 | "@types/d3" -> "5.7.2" 69 | ), 70 | /* we use a bit of functionality which can't be found in scala-js-dom */ 71 | stUseScalaJsDom := false, 72 | useYarn := true, 73 | webpackDevServerPort := 8001 74 | ) 75 | 76 | lazy val jquery = project 77 | .enablePlugins(ScalablyTypedConverterPlugin) 78 | .configure(baseSettings, bundlerSettings, browserProject, withCssLoading) 79 | .settings( 80 | Compile / npmDependencies ++= Seq( 81 | "jquery" -> "3.3", 82 | "@types/jquery" -> "3.3.31", 83 | "jqueryui" -> "1.11.1", 84 | "@types/jqueryui" -> "1.12.10" 85 | ), 86 | useYarn := true, 87 | webpackDevServerPort := 8003 88 | ) 89 | 90 | lazy val `google-maps` = project 91 | .enablePlugins(ScalablyTypedConverterPlugin) 92 | .configure(baseSettings, bundlerSettings, browserProject) 93 | .settings( 94 | Compile / npmDependencies ++= Seq( 95 | "@types/googlemaps" -> "3.39.2" 96 | ), 97 | webpackDevServerPort := 8002, 98 | useYarn := true 99 | ) 100 | 101 | lazy val reveal = project 102 | .enablePlugins(ScalablyTypedConverterPlugin) 103 | .configure(baseSettings, bundlerSettings, browserProject, withCssLoading) 104 | .settings( 105 | Compile / npmDependencies ++= Seq( 106 | "@types/highlight.js" -> "9.12.3", 107 | "@types/reveal" -> "3.3.33", 108 | "highlight.js" -> "9.12", 109 | "reveal.js" -> "3.7.0", 110 | "react" -> "16.9", 111 | "react-dom" -> "16.9" 112 | ), 113 | // note: this demo is not a react demo. It doesn't use any typescript react components, it's just used to render 114 | stIgnore ++= List("react", "react-dom", "reveal.js"), 115 | stFlavour := Flavour.ScalajsReact, 116 | useYarn := true, 117 | webpackDevServerPort := 8006 118 | ) 119 | 120 | lazy val chart = project 121 | .enablePlugins(ScalablyTypedConverterPlugin) 122 | .configure(baseSettings, bundlerSettings, browserProject) 123 | .settings( 124 | Compile / npmDependencies ++= Seq( 125 | "@types/chart.js" -> "2.9.11", 126 | "chart.js" -> "2.9.3" 127 | ), 128 | stUseScalaJsDom := false, 129 | useYarn := true, 130 | webpackDevServerPort := 8007 131 | ) 132 | 133 | lazy val p5 = project 134 | .enablePlugins(ScalablyTypedConverterPlugin) 135 | .configure(baseSettings, bundlerSettings, browserProject) 136 | .settings( 137 | Compile / npmDependencies ++= Seq( 138 | "@types/p5" -> "0.9.0", 139 | "p5" -> "0.9" 140 | ), 141 | useYarn := true, 142 | webpackDevServerPort := 8009 143 | ) 144 | 145 | lazy val leaflet = project 146 | .enablePlugins(ScalablyTypedConverterPlugin) 147 | .configure(baseSettings, bundlerSettings, browserProject) 148 | .settings( 149 | Compile / npmDependencies ++= Seq( 150 | "leaflet" -> "1.9.2", 151 | "@types/leaflet" -> "1.9.0" 152 | ), 153 | useYarn := true, 154 | webpackDevServerPort := 8010 155 | ) 156 | 157 | lazy val angular = project 158 | .enablePlugins(ScalablyTypedConverterPlugin) 159 | .configure(baseSettings, bundlerSettings, browserProject, withCssLoading) 160 | .settings( 161 | Compile / npmDependencies ++= Seq( 162 | "@angular/common" -> "8.2.14", 163 | "@angular/compiler" -> "8.2.14", 164 | "@angular/core" -> "8.2.14", 165 | "@angular/forms" -> "8.2.14", 166 | "@angular/platform-browser-dynamic" -> "8.2.14", 167 | "@angular/platform-browser" -> "8.2.14", 168 | "@angular/router" -> "8.2.14", 169 | "core-js" -> "2.6.8", 170 | "rxjs" -> "6.5.4", 171 | "tslib" -> "1.10.0", 172 | "zone.js" -> "0.9.1" 173 | ), 174 | stEnableScalaJsDefined := Selection.NoneExcept("@angular/core"), 175 | stIgnore := List( 176 | /* this shouldn't be used directly */ 177 | "@angular/compiler", 178 | /* not very interesting */ 179 | "core-js" 180 | ), 181 | useYarn := true, 182 | webpackDevServerPort := 8008 183 | ) 184 | 185 | lazy val onsenui = 186 | project 187 | .enablePlugins(ScalablyTypedConverterPlugin) 188 | .configure(baseSettings, bundlerSettings, browserProject) 189 | .settings( 190 | Compile / npmDependencies ++= Seq( 191 | "@types/jquery" -> "3.3.31", 192 | "jquery" -> "3.3", 193 | "onsenui" -> "2.10.10" 194 | ), 195 | useYarn := true, 196 | webpackDevServerPort := 8011 197 | ) 198 | 199 | lazy val phaser = 200 | project 201 | .enablePlugins(ScalablyTypedConverterPlugin) 202 | .configure(baseSettings, bundlerSettings, browserProject, withCssLoading) 203 | .settings( 204 | Compile / npmDependencies ++= Seq("phaser" -> "3.22.0"), 205 | useYarn := true, 206 | webpackDevServerPort := 8012 207 | ) 208 | 209 | lazy val pixi = project 210 | .enablePlugins(ScalablyTypedConverterPlugin) 211 | .configure(baseSettings, bundlerSettings, browserProject, withCssLoading) 212 | .settings( 213 | Compile / npmDependencies ++= Seq( 214 | "pixi.js" -> "5.2.1", 215 | "pixi-filters" -> "3.1.0", 216 | "@types/highlight.js" -> "9.12.3", 217 | "highlight.js" -> "9.12" 218 | ), 219 | useYarn := true, 220 | webpackDevServerPort := 8013 221 | ) 222 | 223 | lazy val cypress = project 224 | .enablePlugins(ScalablyTypedConverterPlugin) 225 | .configure(baseSettings, bundlerSettings) 226 | .settings( 227 | Compile / npmDependencies ++= Seq( 228 | "cypress" -> "8.5.0", 229 | ), 230 | useYarn := true, 231 | run := { 232 | (Compile / npmInstallDependencies).value 233 | (Compile / fastOptJS).value 234 | val cypressRunner: File = (Compile / npmUpdate / crossTarget).value / "node_modules" / ".bin" / "cypress" 235 | if (Process(s"$cypressRunner run", baseDirectory.value).run().exitValue() != 0) throw new RuntimeException("failed") 236 | } 237 | ) 238 | 239 | lazy val electron = project 240 | .enablePlugins(ScalablyTypedConverterExternalNpmPlugin) 241 | .configure(baseSettings) 242 | .settings( 243 | stStdlib := List("es5"), // doesn't include DOM 244 | /** 245 | * ScalablyTypedConverterExternalNpmPlugin requires that we define how to install node dependencies and where they are 246 | * 247 | * Since we run yarn ourselves the dependencies live in electron/package.json 248 | * 249 | * Invoking yarn is somewhat awkward, because for instance sbt started through intellij won't have a proper PATH set. 250 | * try to start an interactive shell to read the necessary dot files 251 | */ 252 | externalNpm := { 253 | if (scala.util.Properties.isWin) Process("yarn", baseDirectory.value).run() 254 | else Process("bash -ci 'yarn'", baseDirectory.value).run() 255 | baseDirectory.value 256 | }, 257 | jsEnv := new NodeJSEnv( 258 | NodeJSEnv 259 | .Config() 260 | .withExecutable("electron/node_modules/.bin/electron") 261 | .withArgs(List((Compile / classDirectory).value.toString)) 262 | ) 263 | ) 264 | 265 | lazy val lodash = 266 | project 267 | .enablePlugins(ScalablyTypedConverterPlugin) 268 | .configure(baseSettings, bundlerSettings, nodeProject) 269 | .settings( 270 | Compile / npmDependencies ++= Seq( 271 | "@types/lodash" -> "4.14.149", 272 | "lodash" -> "4.17.11" 273 | ), 274 | useYarn := true 275 | ) 276 | 277 | lazy val `node-express` = 278 | project 279 | .enablePlugins(ScalablyTypedConverterPlugin) 280 | .configure(baseSettings, bundlerSettings, nodeProject) 281 | .settings( 282 | Compile / npmDependencies ++= Seq( 283 | "@types/express" -> "4.17.14", 284 | "express" -> "4.18.2" 285 | ), 286 | useYarn := true 287 | ) 288 | 289 | lazy val typescript = 290 | project 291 | .enablePlugins(ScalablyTypedConverterPlugin) 292 | .configure(baseSettings, bundlerSettings, nodeProject) 293 | .settings( 294 | Compile / npmDependencies ++= Seq( 295 | "typescript" -> "3.8.3" 296 | ), 297 | /* typescript is implicitly added by the plugin since that's where we get the files for stdlib, and also implicitly ignored */ 298 | stIgnore ~= (_.filterNot(_ == "typescript")), 299 | useYarn := true 300 | ) 301 | 302 | lazy val baseSettings: Project => Project = 303 | _.enablePlugins(ScalaJSPlugin) 304 | .settings( 305 | scalaVersion := "3.3.0", 306 | version := "0.1-SNAPSHOT", 307 | scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked"), 308 | scalaJSUseMainModuleInitializer := true, 309 | scalaJSLinkerConfig ~= (_ 310 | /* disabled because it somehow triggers many warnings */ 311 | .withSourceMap(false) 312 | .withModuleKind(ModuleKind.CommonJSModule)) 313 | ) 314 | 315 | lazy val bundlerSettings: Project => Project = 316 | _.settings( 317 | webpackCliVersion := "4.10.0", 318 | webpack / version := "5.88.2", 319 | Compile / fastOptJS / webpackExtraArgs += "--mode=development", 320 | Compile / fullOptJS / webpackExtraArgs += "--mode=production", 321 | Compile / fastOptJS / webpackDevServerExtraArgs += "--mode=development", 322 | Compile / fullOptJS / webpackDevServerExtraArgs += "--mode=production" 323 | ) 324 | 325 | val nodeProject: Project => Project = 326 | _.settings( 327 | jsEnv := new org.scalajs.jsenv.nodejs.NodeJSEnv, 328 | // this doesn't include DOM, which we don't have access to in node. It does, however use BigInt, which is needed 329 | stStdlib := List("esnext"), 330 | stUseScalaJsDom := false, 331 | Compile / npmDependencies ++= Seq( 332 | "@types/node" -> "18.11.9" 333 | ) 334 | ) 335 | 336 | lazy val withCssLoading: Project => Project = 337 | _.settings( 338 | /* custom webpack file to include css */ 339 | webpackConfigFile := Some((ThisBuild / baseDirectory).value / "custom.webpack.config.js"), 340 | Compile / npmDevDependencies ++= Seq( 341 | "webpack-merge" -> "5.9.0", 342 | "css-loader" -> "6.8.1", 343 | "style-loader" -> "3.3.3", 344 | "file-loader" -> "6.2.0", 345 | "url-loader" -> "4.1.1" 346 | ) 347 | ) 348 | 349 | /** 350 | * Implement the `start` and `dist` tasks defined above. 351 | * Most of this is really just to copy the index.html file around. 352 | */ 353 | lazy val browserProject: Project => Project = 354 | _.settings( 355 | start := { 356 | (Compile / fastOptJS / startWebpackDevServer).value 357 | }, 358 | dist := { 359 | val artifacts = (Compile / fullOptJS / webpack).value 360 | val artifactFolder = (Compile / fullOptJS / crossTarget).value 361 | val distFolder = (ThisBuild / baseDirectory).value / "docs" / moduleName.value 362 | 363 | distFolder.mkdirs() 364 | artifacts.foreach { artifact => 365 | val target = artifact.data.relativeTo(artifactFolder) match { 366 | case None => distFolder / artifact.data.name 367 | case Some(relFile) => distFolder / relFile.toString 368 | } 369 | 370 | Files.copy(artifact.data.toPath, target.toPath, REPLACE_EXISTING) 371 | } 372 | 373 | val indexFrom = baseDirectory.value / "src/main/js/index.html" 374 | val indexTo = distFolder / "index.html" 375 | 376 | val indexPatchedContent = { 377 | import collection.JavaConverters._ 378 | Files 379 | .readAllLines(indexFrom.toPath, IO.utf8) 380 | .asScala 381 | .map(_.replaceAllLiterally("-fastopt-", "-opt-")) 382 | .mkString("\n") 383 | } 384 | 385 | Files.write(indexTo.toPath, indexPatchedContent.getBytes(IO.utf8)) 386 | distFolder 387 | } 388 | ) 389 | -------------------------------------------------------------------------------- /chart/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chart.js demo 6 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /chart/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import typings.chartJs.mod.{^ as Chart, *} 4 | import typings.moment.mod.Moment 5 | import typings.std.global.document 6 | import typings.std.{stdStrings, Date, HTMLButtonElement, HTMLCanvasElement, HTMLDivElement, MouseEvent} 7 | 8 | import scala.scalajs.js 9 | import scala.scalajs.js.| 10 | import scala.util.Random 11 | 12 | val random = new Random() 13 | 14 | @main 15 | def main(): Unit = 16 | val section = document.createElement_section(stdStrings.section) 17 | section.className = "w" 18 | section.append( 19 | chart(chartConfig(ChartType.bar, randomData(100, random.nextInt()))), 20 | chart(chartConfig(ChartType.pie, randomData(100, random.nextInt()))), 21 | chart(chartConfig(ChartType.polarArea, randomData(100, random.nextInt()))), 22 | chart(chartConfig(ChartType.line, randomData(100, random.nextInt()))) 23 | ) 24 | 25 | document.body.append(section) 26 | end main 27 | 28 | def chartConfig(tpe: ChartType, Data: js.Array[js.UndefOr[ChartPoint | Double | Null]]): ChartConfiguration = 29 | ChartConfiguration() 30 | .setType(tpe) 31 | .setData( 32 | ChartData() 33 | .setLabels(Labels) 34 | .setDatasets( 35 | js.Array( 36 | ChartDataSets() 37 | .setLabel("Dataset 1") 38 | .setData(Data) 39 | .setBorderWidth(1) 40 | .setBackgroundColor(BackgroundColor) 41 | .setBorderColor(BorderColor) 42 | ) 43 | ) 44 | ) 45 | .setOptions(ChartOptions().setResponsive(true)) 46 | 47 | def chart(config: ChartConfiguration): HTMLDivElement = 48 | val div: HTMLDivElement = document.createElement_div(stdStrings.div) 49 | val canvas: HTMLCanvasElement = document.createElement_canvas(stdStrings.canvas) 50 | val chart: Chart = new Chart(canvas, config) 51 | 52 | def dataSetsU: js.UndefOr[js.Array[ChartDataSets]] = 53 | config.data.flatMap(_.datasets) 54 | 55 | val randomizeBtn = button("Randomize Data") { (_, _) => 56 | dataSetsU.foreach(_.foreach(dataset => dataset.data = randomData(100, random.nextInt()))) 57 | chart.update() 58 | } 59 | 60 | val addDataSet = button("Add Dataset") { (_, _) => 61 | val newDataset = ChartDataSets() 62 | .setLabel("Dataset " + dataSetsU.fold(0)(_.length + 1)) 63 | .setData(randomData(100, random.nextInt())) 64 | .setBorderWidth(1) 65 | .setBackgroundColor(BackgroundColor) 66 | .setBorderColor(BorderColor) 67 | 68 | dataSetsU.foreach(_.push(newDataset)) 69 | chart.update() 70 | } 71 | 72 | val removeDataset = button("Remove dataset") { (_, _) => 73 | dataSetsU.foreach(_.splice(0, 1)) 74 | chart.update() 75 | } 76 | 77 | div.append(canvas, randomizeBtn, addDataSet, removeDataset) 78 | div 79 | end chart 80 | 81 | def button(title: String)(onClick: js.ThisFunction1[HTMLButtonElement, MouseEvent, js.Any]): HTMLButtonElement = 82 | val btn = document.createElement_button(stdStrings.button) 83 | btn.textContent = title 84 | btn.addEventListener_click(stdStrings.click, onClick) 85 | btn 86 | end button 87 | 88 | def randomData(max: Int, seed: Int): js.Array[js.UndefOr[ChartPoint | Double | scala.Null]] = 89 | val random = new Random(seed) 90 | js.Array[js.UndefOr[ChartPoint | Double | Null]]( 91 | random.nextInt(max), 92 | random.nextInt(max), 93 | random.nextInt(max), 94 | random.nextInt(max), 95 | random.nextInt(max), 96 | random.nextInt(max) 97 | ) 98 | end randomData 99 | 100 | val Labels: js.Array[String | js.Array[js.Date | Double | Moment | String] | Double | js.Date | Moment] = 101 | js.Array("Red", "Blue", "Yellow", "Green", "Purple", "Orange") 102 | 103 | val BackgroundColor: ChartColor = 104 | js.Array( 105 | color(54, 162, 235, 0.2), 106 | color(255, 206, 86, 0.2), 107 | color(75, 192, 192, 0.2), 108 | color(153, 102, 255, 0.2), 109 | color(255, 159, 64, 0.2) 110 | ) 111 | 112 | val BorderColor: ChartColor = 113 | js.Array( 114 | color(255, 99, 132, 1), 115 | color(54, 162, 235, 1), 116 | color(255, 206, 86, 1), 117 | color(75, 192, 192, 1), 118 | color(153, 102, 255, 1), 119 | color(255, 159, 64, 1) 120 | ) 121 | 122 | def color(r: Int, g: Int, b: Int, a: Double): String = 123 | s"rgba($r, $g, $b, $a)" 124 | -------------------------------------------------------------------------------- /custom.webpack.config.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge').merge; 2 | var generated = require('./scalajs.webpack.config'); 3 | var path = require('path'); 4 | 5 | var local = { 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: ['style-loader', 'css-loader'] 11 | }, 12 | { 13 | test: /\.(ttf|eot|woff|png|glb|jpeg|jpg|mp4|jsn)$/, 14 | use: 'file-loader' 15 | }, 16 | { 17 | test: /\.(eot)$/, 18 | use: 'url-loader' 19 | } 20 | ] 21 | } 22 | }; 23 | 24 | module.exports = merge(generated, local); 25 | -------------------------------------------------------------------------------- /cypress/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "integrationFolder": "target/scala-3.1.0/cypress-fastopt", 3 | "testFiles": "main.js" 4 | } 5 | -------------------------------------------------------------------------------- /cypress/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | } 23 | -------------------------------------------------------------------------------- /cypress/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /cypress/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /cypress/cypress/videos/main.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/cypress/cypress/videos/main.js.mp4 -------------------------------------------------------------------------------- /cypress/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import typings.cypress.global.{describe, it, cy} 2 | 3 | @main 4 | def main: Unit = 5 | describe("My First Test", (suite) => { 6 | it("finds the content 'type'", (ctx, done) => { 7 | cy.visit("https://example.cypress.io"); 8 | cy.contains("type") 9 | done(()) 10 | }) 11 | }) 12 | end main 13 | -------------------------------------------------------------------------------- /d3/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | D3 demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /d3/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import typings.d3.mod as d3Mod 4 | import typings.d3Geo.mod.{GeoContext, GeoPermissibleObjects, GeoProjection_, GeoPath_} 5 | import typings.geojson.geojsonStrings 6 | import typings.geojson.mod.{LineString, Position} 7 | import typings.std.global.{console, document, window} 8 | import typings.std.{CanvasRenderingContext2D, FrameRequestCallback, HTMLCanvasElement, stdStrings} 9 | 10 | import scala.scalajs.js 11 | import scala.scalajs.js.| 12 | 13 | // where is my `?.` :( :( 14 | extension [T](ot: T | Null) 15 | def andThen[U](f: T => U | Null): U | Null = 16 | if ot != null then f(ot.asInstanceOf[T]) else null // todo: revisit with explicit nulls 17 | 18 | // this conforms structurally 19 | def isGeoContext(ctx: CanvasRenderingContext2D): GeoContext = 20 | ctx.asInstanceOf[GeoContext] 21 | 22 | @main 23 | def main(): Unit = 24 | val maybeContext: CanvasRenderingContext2D | Null = 25 | document 26 | .getElementsByTagName_canvas(stdStrings.canvas) 27 | .item(0) 28 | .andThen(_.getContext_2d(stdStrings.`2d`)) 29 | 30 | if maybeContext != null then start(maybeContext) 31 | else sys.error("Cannot get 2d context") 32 | end main 33 | 34 | def start(context: CanvasRenderingContext2D): Double = 35 | context.lineWidth = 0.4 36 | context.strokeStyle = "rgba(255, 255, 255, 0.6)" 37 | 38 | val width = window.innerWidth 39 | val height = window.innerHeight 40 | val size = width min height 41 | 42 | d3Mod 43 | .select("#content") 44 | .attr("width", s"${width}px") 45 | .attr("height", s"${height}px") 46 | 47 | val projection: GeoProjection_ = 48 | d3Mod 49 | .geoOrthographic() 50 | .scale(0.45 * size) 51 | .translate(js.Tuple2(0.5 * width, 0.5 * height)) 52 | 53 | val geoGenerator: GeoPath_[Any, GeoPermissibleObjects] = 54 | d3Mod.geoPath(projection, isGeoContext(context)) 55 | 56 | val geometry = LineString(coordinates = js.Array[Position]()) 57 | 58 | def rndLon = -180 + Math.random() * 360 59 | def rndLat = -90 + Math.random() * 180 60 | 61 | def addPoint(): Unit = 62 | geometry.coordinates.push(js.Array(rndLon, rndLat)) 63 | 64 | def update: FrameRequestCallback = 65 | (time: Double) => 66 | if geometry.coordinates.length < 6000 then addPoint() 67 | 68 | projection.rotate(js.Tuple2(time / 100, 1.0)) 69 | 70 | context.clearRect(0, 0, width, height) 71 | context.beginPath() 72 | 73 | geoGenerator(geometry, null.asInstanceOf[js.Any]) 74 | context.stroke() 75 | 76 | window.requestAnimationFrame(update) 77 | 78 | window.requestAnimationFrame(update) 79 | end start 80 | -------------------------------------------------------------------------------- /docs/angular/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Angular with Scala.js 7 | 8 | 9 |

Sorry, angular demo is currently broken

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/chart/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chart.js demo 6 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/d3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | D3 demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/google-maps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Google maps demo 3 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/jquery/05d14cb826bafefd5b46fa2e3be5c952.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/05d14cb826bafefd5b46fa2e3be5c952.png -------------------------------------------------------------------------------- /docs/jquery/0f8012f9214f21e71d2e09dd59620cff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/0f8012f9214f21e71d2e09dd59620cff.png -------------------------------------------------------------------------------- /docs/jquery/3676d240bfc41073d4b9a3886dbb2c2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/3676d240bfc41073d4b9a3886dbb2c2d.png -------------------------------------------------------------------------------- /docs/jquery/3e7b25be8db5575f37ef732e60ea6eb4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/3e7b25be8db5575f37ef732e60ea6eb4.png -------------------------------------------------------------------------------- /docs/jquery/546138d93ba6d3da76e1cefdd3493b25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/546138d93ba6d3da76e1cefdd3493b25.png -------------------------------------------------------------------------------- /docs/jquery/5c18c7c3b45a9e304e1da3da3f6c517f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/5c18c7c3b45a9e304e1da3da3f6c517f.png -------------------------------------------------------------------------------- /docs/jquery/5e67153340a01757d1ee073aa6fdbdd8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/5e67153340a01757d1ee073aa6fdbdd8.png -------------------------------------------------------------------------------- /docs/jquery/6451ae9a01ac65bd0fd7eb118e7770f1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/6451ae9a01ac65bd0fd7eb118e7770f1.png -------------------------------------------------------------------------------- /docs/jquery/6616f7b7e32f5346b03964406a933d49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/6616f7b7e32f5346b03964406a933d49.png -------------------------------------------------------------------------------- /docs/jquery/6b0513b86cddf85f3764609bbfedfd79.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/6b0513b86cddf85f3764609bbfedfd79.png -------------------------------------------------------------------------------- /docs/jquery/7ebbd63f8c28bf02ab557026bb078116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/7ebbd63f8c28bf02ab557026bb078116.png -------------------------------------------------------------------------------- /docs/jquery/aabe52d4b720fc416d5fa1bf79849f8c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/aabe52d4b720fc416d5fa1bf79849f8c.png -------------------------------------------------------------------------------- /docs/jquery/b159d479704de144f3bde7eb8c232ce5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/b159d479704de144f3bde7eb8c232ce5.png -------------------------------------------------------------------------------- /docs/jquery/b5c094c2859bb622056ba7a883112aec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/jquery/b5c094c2859bb622056ba7a883112aec.png -------------------------------------------------------------------------------- /docs/jquery/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jquery demo 6 | 7 | 8 |
9 | 10 | 11 | 12 |

Jqueryui accordion:

13 |
14 |

Section 1

15 |
16 |

17 | Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer 18 | ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit 19 | amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut 20 | odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. 21 |

22 |
23 |

Section 2

24 |
25 |

26 | Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet 27 | purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor 28 | velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In 29 | suscipit faucibus urna. 30 |

31 |
32 |

Section 3

33 |
34 |

35 | Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. 36 | Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero 37 | ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis 38 | lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. 39 |

40 |
    41 |
  • List item one
  • 42 |
  • List item two
  • 43 |
  • List item three
  • 44 |
45 |
46 |

Section 4

47 |
48 |

49 | Cras dictum. Pellentesque habitant morbi tristique senectus et netus 50 | et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in 51 | faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia 52 | mauris vel est. 53 |

54 |

55 | Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. 56 | Class aptent taciti sociosqu ad litora torquent per conubia nostra, per 57 | inceptos himenaeos. 58 |

59 |
60 |
61 |
62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/leaflet/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | leaflet demo 6 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/onsenui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Click me! 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/p5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | P5 demo 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/phaser/4c749ba5f2242b64e37fa7439ece8c98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/phaser/4c749ba5f2242b64e37fa7439ece8c98.png -------------------------------------------------------------------------------- /docs/phaser/ff69a6fb51aef4d4950ff9398136e979.jsn: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "diamond_0000": 4 | { 5 | "frame": {"x":2,"y":2,"w":64,"h":64}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 9 | "sourceSize": {"w":64,"h":64}, 10 | "pivot": {"x":0.5,"y":0.5} 11 | }, 12 | "diamond_0001": 13 | { 14 | "frame": {"x":2,"y":68,"w":64,"h":64}, 15 | "rotated": false, 16 | "trimmed": false, 17 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 18 | "sourceSize": {"w":64,"h":64}, 19 | "pivot": {"x":0.5,"y":0.5} 20 | }, 21 | "diamond_0002": 22 | { 23 | "frame": {"x":2,"y":134,"w":64,"h":64}, 24 | "rotated": false, 25 | "trimmed": false, 26 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 27 | "sourceSize": {"w":64,"h":64}, 28 | "pivot": {"x":0.5,"y":0.5} 29 | }, 30 | "diamond_0003": 31 | { 32 | "frame": {"x":68,"y":2,"w":64,"h":64}, 33 | "rotated": false, 34 | "trimmed": false, 35 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 36 | "sourceSize": {"w":64,"h":64}, 37 | "pivot": {"x":0.5,"y":0.5} 38 | }, 39 | "diamond_0004": 40 | { 41 | "frame": {"x":68,"y":68,"w":64,"h":64}, 42 | "rotated": false, 43 | "trimmed": false, 44 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 45 | "sourceSize": {"w":64,"h":64}, 46 | "pivot": {"x":0.5,"y":0.5} 47 | }, 48 | "diamond_0005": 49 | { 50 | "frame": {"x":68,"y":134,"w":64,"h":64}, 51 | "rotated": false, 52 | "trimmed": false, 53 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 54 | "sourceSize": {"w":64,"h":64}, 55 | "pivot": {"x":0.5,"y":0.5} 56 | }, 57 | "diamond_0006": 58 | { 59 | "frame": {"x":134,"y":2,"w":64,"h":64}, 60 | "rotated": false, 61 | "trimmed": false, 62 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 63 | "sourceSize": {"w":64,"h":64}, 64 | "pivot": {"x":0.5,"y":0.5} 65 | }, 66 | "diamond_0007": 67 | { 68 | "frame": {"x":134,"y":68,"w":64,"h":64}, 69 | "rotated": false, 70 | "trimmed": false, 71 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 72 | "sourceSize": {"w":64,"h":64}, 73 | "pivot": {"x":0.5,"y":0.5} 74 | }, 75 | "diamond_0008": 76 | { 77 | "frame": {"x":134,"y":134,"w":64,"h":64}, 78 | "rotated": false, 79 | "trimmed": false, 80 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 81 | "sourceSize": {"w":64,"h":64}, 82 | "pivot": {"x":0.5,"y":0.5} 83 | }, 84 | "diamond_0009": 85 | { 86 | "frame": {"x":200,"y":2,"w":64,"h":64}, 87 | "rotated": false, 88 | "trimmed": false, 89 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 90 | "sourceSize": {"w":64,"h":64}, 91 | "pivot": {"x":0.5,"y":0.5} 92 | }, 93 | "diamond_0010": 94 | { 95 | "frame": {"x":200,"y":68,"w":64,"h":64}, 96 | "rotated": false, 97 | "trimmed": false, 98 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 99 | "sourceSize": {"w":64,"h":64}, 100 | "pivot": {"x":0.5,"y":0.5} 101 | }, 102 | "diamond_0011": 103 | { 104 | "frame": {"x":200,"y":134,"w":64,"h":64}, 105 | "rotated": false, 106 | "trimmed": false, 107 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 108 | "sourceSize": {"w":64,"h":64}, 109 | "pivot": {"x":0.5,"y":0.5} 110 | }, 111 | "diamond_0012": 112 | { 113 | "frame": {"x":266,"y":2,"w":64,"h":64}, 114 | "rotated": false, 115 | "trimmed": false, 116 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 117 | "sourceSize": {"w":64,"h":64}, 118 | "pivot": {"x":0.5,"y":0.5} 119 | }, 120 | "diamond_0013": 121 | { 122 | "frame": {"x":266,"y":68,"w":64,"h":64}, 123 | "rotated": false, 124 | "trimmed": false, 125 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 126 | "sourceSize": {"w":64,"h":64}, 127 | "pivot": {"x":0.5,"y":0.5} 128 | }, 129 | "diamond_0014": 130 | { 131 | "frame": {"x":266,"y":134,"w":64,"h":64}, 132 | "rotated": false, 133 | "trimmed": false, 134 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 135 | "sourceSize": {"w":64,"h":64}, 136 | "pivot": {"x":0.5,"y":0.5} 137 | }, 138 | "diamond_0015": 139 | { 140 | "frame": {"x":332,"y":2,"w":64,"h":64}, 141 | "rotated": false, 142 | "trimmed": false, 143 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 144 | "sourceSize": {"w":64,"h":64}, 145 | "pivot": {"x":0.5,"y":0.5} 146 | }, 147 | "prism_0000": 148 | { 149 | "frame": {"x":332,"y":68,"w":64,"h":64}, 150 | "rotated": false, 151 | "trimmed": false, 152 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 153 | "sourceSize": {"w":64,"h":64}, 154 | "pivot": {"x":0.5,"y":0.5} 155 | }, 156 | "prism_0001": 157 | { 158 | "frame": {"x":332,"y":134,"w":64,"h":64}, 159 | "rotated": false, 160 | "trimmed": false, 161 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 162 | "sourceSize": {"w":64,"h":64}, 163 | "pivot": {"x":0.5,"y":0.5} 164 | }, 165 | "prism_0002": 166 | { 167 | "frame": {"x":398,"y":2,"w":64,"h":64}, 168 | "rotated": false, 169 | "trimmed": false, 170 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 171 | "sourceSize": {"w":64,"h":64}, 172 | "pivot": {"x":0.5,"y":0.5} 173 | }, 174 | "prism_0003": 175 | { 176 | "frame": {"x":398,"y":68,"w":64,"h":64}, 177 | "rotated": false, 178 | "trimmed": false, 179 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 180 | "sourceSize": {"w":64,"h":64}, 181 | "pivot": {"x":0.5,"y":0.5} 182 | }, 183 | "prism_0004": 184 | { 185 | "frame": {"x":398,"y":134,"w":64,"h":64}, 186 | "rotated": false, 187 | "trimmed": false, 188 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 189 | "sourceSize": {"w":64,"h":64}, 190 | "pivot": {"x":0.5,"y":0.5} 191 | }, 192 | "prism_0005": 193 | { 194 | "frame": {"x":464,"y":2,"w":64,"h":64}, 195 | "rotated": false, 196 | "trimmed": false, 197 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 198 | "sourceSize": {"w":64,"h":64}, 199 | "pivot": {"x":0.5,"y":0.5} 200 | }, 201 | "prism_0006": 202 | { 203 | "frame": {"x":464,"y":68,"w":64,"h":64}, 204 | "rotated": false, 205 | "trimmed": false, 206 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 207 | "sourceSize": {"w":64,"h":64}, 208 | "pivot": {"x":0.5,"y":0.5} 209 | }, 210 | "ruby_0000": 211 | { 212 | "frame": {"x":464,"y":134,"w":64,"h":64}, 213 | "rotated": false, 214 | "trimmed": false, 215 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 216 | "sourceSize": {"w":64,"h":64}, 217 | "pivot": {"x":0.5,"y":0.5} 218 | }, 219 | "ruby_0001": 220 | { 221 | "frame": {"x":530,"y":2,"w":64,"h":64}, 222 | "rotated": false, 223 | "trimmed": false, 224 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 225 | "sourceSize": {"w":64,"h":64}, 226 | "pivot": {"x":0.5,"y":0.5} 227 | }, 228 | "ruby_0002": 229 | { 230 | "frame": {"x":530,"y":68,"w":64,"h":64}, 231 | "rotated": false, 232 | "trimmed": false, 233 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 234 | "sourceSize": {"w":64,"h":64}, 235 | "pivot": {"x":0.5,"y":0.5} 236 | }, 237 | "ruby_0003": 238 | { 239 | "frame": {"x":530,"y":134,"w":64,"h":64}, 240 | "rotated": false, 241 | "trimmed": false, 242 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 243 | "sourceSize": {"w":64,"h":64}, 244 | "pivot": {"x":0.5,"y":0.5} 245 | }, 246 | "ruby_0004": 247 | { 248 | "frame": {"x":596,"y":2,"w":64,"h":64}, 249 | "rotated": false, 250 | "trimmed": false, 251 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 252 | "sourceSize": {"w":64,"h":64}, 253 | "pivot": {"x":0.5,"y":0.5} 254 | }, 255 | "ruby_0005": 256 | { 257 | "frame": {"x":596,"y":68,"w":64,"h":64}, 258 | "rotated": false, 259 | "trimmed": false, 260 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 261 | "sourceSize": {"w":64,"h":64}, 262 | "pivot": {"x":0.5,"y":0.5} 263 | }, 264 | "ruby_0006": 265 | { 266 | "frame": {"x":596,"y":134,"w":64,"h":64}, 267 | "rotated": false, 268 | "trimmed": false, 269 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 270 | "sourceSize": {"w":64,"h":64}, 271 | "pivot": {"x":0.5,"y":0.5} 272 | }, 273 | "square_0000": 274 | { 275 | "frame": {"x":662,"y":2,"w":64,"h":64}, 276 | "rotated": false, 277 | "trimmed": false, 278 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 279 | "sourceSize": {"w":64,"h":64}, 280 | "pivot": {"x":0.5,"y":0.5} 281 | }, 282 | "square_0001": 283 | { 284 | "frame": {"x":662,"y":68,"w":64,"h":64}, 285 | "rotated": false, 286 | "trimmed": false, 287 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 288 | "sourceSize": {"w":64,"h":64}, 289 | "pivot": {"x":0.5,"y":0.5} 290 | }, 291 | "square_0002": 292 | { 293 | "frame": {"x":662,"y":134,"w":64,"h":64}, 294 | "rotated": false, 295 | "trimmed": false, 296 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 297 | "sourceSize": {"w":64,"h":64}, 298 | "pivot": {"x":0.5,"y":0.5} 299 | }, 300 | "square_0003": 301 | { 302 | "frame": {"x":728,"y":2,"w":64,"h":64}, 303 | "rotated": false, 304 | "trimmed": false, 305 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 306 | "sourceSize": {"w":64,"h":64}, 307 | "pivot": {"x":0.5,"y":0.5} 308 | }, 309 | "square_0004": 310 | { 311 | "frame": {"x":728,"y":68,"w":64,"h":64}, 312 | "rotated": false, 313 | "trimmed": false, 314 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 315 | "sourceSize": {"w":64,"h":64}, 316 | "pivot": {"x":0.5,"y":0.5} 317 | }, 318 | "square_0005": 319 | { 320 | "frame": {"x":728,"y":134,"w":64,"h":64}, 321 | "rotated": false, 322 | "trimmed": false, 323 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 324 | "sourceSize": {"w":64,"h":64}, 325 | "pivot": {"x":0.5,"y":0.5} 326 | }, 327 | "square_0006": 328 | { 329 | "frame": {"x":794,"y":2,"w":64,"h":64}, 330 | "rotated": false, 331 | "trimmed": false, 332 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 333 | "sourceSize": {"w":64,"h":64}, 334 | "pivot": {"x":0.5,"y":0.5} 335 | }, 336 | "square_0007": 337 | { 338 | "frame": {"x":860,"y":2,"w":64,"h":64}, 339 | "rotated": false, 340 | "trimmed": false, 341 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 342 | "sourceSize": {"w":64,"h":64}, 343 | "pivot": {"x":0.5,"y":0.5} 344 | }, 345 | "square_0008": 346 | { 347 | "frame": {"x":926,"y":2,"w":64,"h":64}, 348 | "rotated": false, 349 | "trimmed": false, 350 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 351 | "sourceSize": {"w":64,"h":64}, 352 | "pivot": {"x":0.5,"y":0.5} 353 | }, 354 | "square_0009": 355 | { 356 | "frame": {"x":794,"y":68,"w":64,"h":64}, 357 | "rotated": false, 358 | "trimmed": false, 359 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 360 | "sourceSize": {"w":64,"h":64}, 361 | "pivot": {"x":0.5,"y":0.5} 362 | }, 363 | "square_0010": 364 | { 365 | "frame": {"x":794,"y":134,"w":64,"h":64}, 366 | "rotated": false, 367 | "trimmed": false, 368 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 369 | "sourceSize": {"w":64,"h":64}, 370 | "pivot": {"x":0.5,"y":0.5} 371 | }, 372 | "square_0011": 373 | { 374 | "frame": {"x":860,"y":68,"w":64,"h":64}, 375 | "rotated": false, 376 | "trimmed": false, 377 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 378 | "sourceSize": {"w":64,"h":64}, 379 | "pivot": {"x":0.5,"y":0.5} 380 | }, 381 | "square_0012": 382 | { 383 | "frame": {"x":926,"y":68,"w":64,"h":64}, 384 | "rotated": false, 385 | "trimmed": false, 386 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 387 | "sourceSize": {"w":64,"h":64}, 388 | "pivot": {"x":0.5,"y":0.5} 389 | }, 390 | "square_0013": 391 | { 392 | "frame": {"x":860,"y":134,"w":64,"h":64}, 393 | "rotated": false, 394 | "trimmed": false, 395 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 396 | "sourceSize": {"w":64,"h":64}, 397 | "pivot": {"x":0.5,"y":0.5} 398 | }, 399 | "square_0014": 400 | { 401 | "frame": {"x":926,"y":134,"w":64,"h":64}, 402 | "rotated": false, 403 | "trimmed": false, 404 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 405 | "sourceSize": {"w":64,"h":64}, 406 | "pivot": {"x":0.5,"y":0.5} 407 | }}, 408 | "meta": { 409 | "app": "http://www.codeandweb.com/texturepacker", 410 | "version": "1.0", 411 | "image": "gems.png", 412 | "format": "RGBA8888", 413 | "size": {"w":992,"h":200}, 414 | "scale": "1", 415 | "smartupdate": "$TexturePacker:SmartUpdate:5dfda9c839d3ca51c00faf9458ba1fca:0b0be3727c36efcd76ae8acf6abbf211:81fc68276d1596f8f7ad75d59a9ce9b5$" 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /docs/phaser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | phaser demo 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/pixi/27905594c5fb6a699e83e2a33ac4594f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/27905594c5fb6a699e83e2a33ac4594f.jpg -------------------------------------------------------------------------------- /docs/pixi/3cd1bac088de1f0141a2d2b460f5e604.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/3cd1bac088de1f0141a2d2b460f5e604.jpeg -------------------------------------------------------------------------------- /docs/pixi/4570873d87d3a0f6e5ac1cb8fe25e0d3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/4570873d87d3a0f6e5ac1cb8fe25e0d3.mp4 -------------------------------------------------------------------------------- /docs/pixi/4570873d87d3a0f6e5ac1cb8fe25e0d3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/4570873d87d3a0f6e5ac1cb8fe25e0d3.png -------------------------------------------------------------------------------- /docs/pixi/671dfaa933552f588d943ca39375807e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/671dfaa933552f588d943ca39375807e.png -------------------------------------------------------------------------------- /docs/pixi/70f6dd03a1f4a89b372199a507d13413.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/70f6dd03a1f4a89b372199a507d13413.mp4 -------------------------------------------------------------------------------- /docs/pixi/81caa7d77cd7fe45112c79d65902fe6f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/81caa7d77cd7fe45112c79d65902fe6f.jpg -------------------------------------------------------------------------------- /docs/pixi/8bfb7c2cc94d5e2fe3e9eb8766c2143c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/8bfb7c2cc94d5e2fe3e9eb8766c2143c.png -------------------------------------------------------------------------------- /docs/pixi/98caa6066cf537444105787d26f45c13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/98caa6066cf537444105787d26f45c13.jpg -------------------------------------------------------------------------------- /docs/pixi/9cc4b590bde4d70f18cb9ba64776ae2b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/9cc4b590bde4d70f18cb9ba64776ae2b.png -------------------------------------------------------------------------------- /docs/pixi/ae108d533b303b523de5393a7e0f4b0e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/ae108d533b303b523de5393a7e0f4b0e.jpeg -------------------------------------------------------------------------------- /docs/pixi/c79a9d4407a11aa2611443c9d8ea7d70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/c79a9d4407a11aa2611443c9d8ea7d70.png -------------------------------------------------------------------------------- /docs/pixi/e6485b0aaa9ff710c4454df3a144efb1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/pixi/e6485b0aaa9ff710c4454df3a144efb1.png -------------------------------------------------------------------------------- /docs/pixi/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixi.js with Scala.js 7 | 8 | 9 | 12 | 13 |
14 |

Pixi.js with Scala.js!

15 | 16 |

17 | Click in the menu on the left to see the examples from the official website translated into 18 | Scala, using Scala.js and ScalablyTyped. 19 |

20 |

21 | This project uses version 5 of Pixi. 22 |

23 |
24 | 25 |
26 |

27 |
28 |

Source code

29 |
30 | See PIXI JavaScript example 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/reveal/1cb8e94f1185f1131a0c895165998f2b.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/1cb8e94f1185f1131a0c895165998f2b.woff -------------------------------------------------------------------------------- /docs/reveal/2da39ecf9246383937da11b44b7bd9b4.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/2da39ecf9246383937da11b44b7bd9b4.ttf -------------------------------------------------------------------------------- /docs/reveal/6b058fc2634b01d837c3432316c3141f.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/6b058fc2634b01d837c3432316c3141f.woff -------------------------------------------------------------------------------- /docs/reveal/8256cfd7e4017a7690814879409212cd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/8256cfd7e4017a7690814879409212cd.ttf -------------------------------------------------------------------------------- /docs/reveal/c7e698a4d0956f4a939f42a05685bbf5.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/c7e698a4d0956f4a939f42a05685bbf5.ttf -------------------------------------------------------------------------------- /docs/reveal/e74f0128884561828ce8c9cf5c284ab8.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/e74f0128884561828ce8c9cf5c284ab8.woff -------------------------------------------------------------------------------- /docs/reveal/e7acc589bb558fe58936a853f570193c.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/e7acc589bb558fe58936a853f570193c.woff -------------------------------------------------------------------------------- /docs/reveal/f3565095e6c9158140444970f5a2c5ed.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/reveal/f3565095e6c9158140444970f5a2c5ed.ttf -------------------------------------------------------------------------------- /docs/reveal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reveal.js demo 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/three/ebf4afeb87874d0a1487e7ea687fb529.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/docs/three/ebf4afeb87874d0a1487e7ea687fb529.glb -------------------------------------------------------------------------------- /docs/three/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Three.js demo 7 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scala.js+Vue.js sample application! 5 | 6 | 38 | 39 | 40 | 41 | 42 |
43 |
44 | Title= 45 | 46 | 47 |
  • 48 | 49 | {{todo.content}} 50 | 51 | 52 |
  • 53 |
    54 | 55 | Tasks# {{todos.length}}
    56 | N={{n}}
    57 |
    58 |
    data JSON: {{$data | json 2}}
    59 | 60 |
    61 | Using a v-text directive title= 62 |
    Smooth CSS animation: 63 | 64 | 65 |
    66 |
    67 |
    68 |
    Title reversed={{title | reverse}}
    69 |
    Title wrapper={{title | wrap('<<','>>')}}
    70 |
    xx
    71 | Special content 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |

    80 | Todos computed: {{ todosComputed }} 81 |
    82 |
    83 | 84 | 85 |
    86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@types/node": "^18.11.9", 4 | "electron": "^21.2.2", 5 | "typescript": "^4.8.4" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /electron/src/main/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello World! 6 | 7 | 8 |

    Hello World!

    9 | We are using node , 10 | Chrome , 11 | and Electron . 12 | 13 | 14 | -------------------------------------------------------------------------------- /electron/src/main/resources/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "your-app", 3 | "version" : "0.1.0", 4 | "main" : "../electron-fastopt/main.js" 5 | } 6 | -------------------------------------------------------------------------------- /electron/src/main/scala/mainprocess/MainProcess.scala: -------------------------------------------------------------------------------- 1 | package mainprocess 2 | 3 | import typings.electron.Electron.BrowserWindowConstructorOptions 4 | import typings.electron.electronStrings 5 | import typings.electron.mod.{BrowserWindow, app} 6 | import typings.node.{urlMod, pathMod as path} 7 | import typings.node.urlMod.UrlObject 8 | 9 | import scala.collection.mutable 10 | import scala.scalajs.js 11 | import scala.scalajs.js.annotation.JSImport 12 | 13 | @JSImport("electron", "BrowserWindow") 14 | @js.native 15 | class FixedBrowserWindow(options: BrowserWindowConstructorOptions) extends typings.electron.mod.BrowserWindow 16 | 17 | /** This object represents the translation into Scala.js of the main.js file presented in the Electron Quick Start 18 | * guide, available at https://electron.atom.io/docs/tutorial/quick-start/ 19 | */ 20 | /** Every created windows will be put into the windows mutable.Set. */ 21 | val windows: mutable.Set[BrowserWindow] = mutable.Set() 22 | 23 | def createWindow(): Unit = 24 | 25 | /** Printing "hello" in the main process, just to see it in action. */ 26 | println("hello") 27 | 28 | /** Creating the BrowserWindow object, with the desired options. 29 | */ 30 | val window = new FixedBrowserWindow( 31 | BrowserWindowConstructorOptions().setHeight(600).setWidth(800) 32 | ) 33 | window.loadURL( 34 | urlMod.format( 35 | UrlObject().setPathname(path.join(app.getAppPath(), "index.html")).setProtocol("file:").setSlashes(true) 36 | ) 37 | ) 38 | 39 | /** In Scala.js, you have two modes for compiling your code. A fast and a full optimized way. Fast optimisation should 40 | * be used in development only. Compilation time is significantly shorted, and to a lesser extent, produces more 41 | * "human readable" JavaScript code in case you really want to have a look. Full optimized compilation leads to more 42 | * optimized JavaScript code, as well as lighter JavaScript files. 43 | * 44 | * As a result, Scala.js also allows you to perform snippets of codes depending on the mode of compilation you chose. 45 | * In this case, we only want to open the dev tools of a window if we are in development mode, but it is something 46 | * that you clearly want to avoid in production code. 47 | */ 48 | if scala.scalajs.LinkingInfo.developmentMode then window.webContents.openDevTools() 49 | end if 50 | 51 | window.on_closed(electronStrings.closed, () => windows -= window) 52 | 53 | windows += window 54 | end createWindow 55 | 56 | /** The main method is called automatically when the JavaScript file is loaded, thanks to the sbt setting 57 | * scalaJSUseMainModuleInitializer := true in the build.sbt file. Removing this method will fail at compile time. 58 | */ 59 | @main 60 | def main: Unit = 61 | app.on_ready(electronStrings.ready, (event, launchInfo) => createWindow()) 62 | app.on_windowallclosed(electronStrings.`window-all-closed`, () => app.quit()) 63 | end main 64 | -------------------------------------------------------------------------------- /google-maps/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Google maps demo 3 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /google-maps/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalajs.dom.document 4 | import org.scalajs.dom.raw.Element 5 | import typings.googlemaps.google.maps.{MapOptions, ReadonlyMarkerOptions} 6 | import typings.googlemaps.global.google.maps 7 | 8 | val beaches: Map[String, maps.LatLng] = 9 | Map( 10 | "Bondi Beach" -> new maps.LatLng(-33.890542, 151.274856), 11 | "Coogee Beach" -> new maps.LatLng(-33.923036, 151.259052), 12 | "Cronulla Beach" -> new maps.LatLng(-34.028249, 151.157507), 13 | "Manly Beach" -> new maps.LatLng(-33.80010128657071, 151.28747820854187) 14 | ) 15 | 16 | @main 17 | def main: Unit = 18 | val container = document.getElementById("content") 19 | val m: maps.Map[Element] = new maps.Map( 20 | container, 21 | MapOptions().setCenter(new maps.LatLng(-33.9, 151.2)).setZoom(4) 22 | ) 23 | 24 | val info = new maps.InfoWindow 25 | 26 | beaches.foreach { case (beach, pos) => 27 | val marker = new maps.Marker(ReadonlyMarkerOptions().setPosition(pos).setTitle(beach).setMap(m)) 28 | 29 | maps.event.addListener( 30 | marker, 31 | "click", 32 | _ => 33 | info.setContent(s"

    This is $beach

    ") 34 | info.open(m, marker) 35 | ) 36 | } 37 | end main 38 | -------------------------------------------------------------------------------- /jquery/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jquery demo 6 | 7 | 8 |
    9 | 10 | 11 | 12 |

    Jqueryui accordion:

    13 |
    14 |

    Section 1

    15 |
    16 |

    17 | Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer 18 | ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit 19 | amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut 20 | odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. 21 |

    22 |
    23 |

    Section 2

    24 |
    25 |

    26 | Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet 27 | purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor 28 | velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In 29 | suscipit faucibus urna. 30 |

    31 |
    32 |

    Section 3

    33 |
    34 |

    35 | Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. 36 | Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero 37 | ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis 38 | lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui. 39 |

    40 |
      41 |
    • List item one
    • 42 |
    • List item two
    • 43 |
    • List item three
    • 44 |
    45 |
    46 |

    Section 4

    47 |
    48 |

    49 | Cras dictum. Pellentesque habitant morbi tristique senectus et netus 50 | et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in 51 | faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia 52 | mauris vel est. 53 |

    54 |

    55 | Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. 56 | Class aptent taciti sociosqu ad litora torquent per conubia nostra, per 57 | inceptos himenaeos. 58 |

    59 |
    60 |
    61 |
    62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /jquery/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import org.scalajs.dom.document 2 | import org.scalajs.dom.html.{Button, Label} 3 | import org.scalajs.dom.raw.Element 4 | import typings.jquery.{JQuery, JQueryEventObject, mod as $} 5 | import typings.jqueryui 6 | import typings.jqueryui.jqueryuiRequire 7 | 8 | import scala.language.implicitConversions 9 | import scala.scalajs.js 10 | import scala.scalajs.js.annotation.JSImport 11 | 12 | @JSImport("jqueryui/jquery-ui.css", JSImport.Namespace) 13 | @js.native 14 | object JqueryUiCss extends js.Object 15 | 16 | /* fake interface augmentation. This is done automatically in typescript. */ 17 | given [T]: Conversion[JQuery[T], typings.jqueryui.JQuery] with 18 | override def apply(x: JQuery[T]): jqueryui.JQuery = x.asInstanceOf[typings.jqueryui.JQuery] 19 | 20 | @main 21 | def main: Unit = 22 | // trigger loading of global library and CSS 23 | jqueryuiRequire 24 | JqueryUiCss 25 | 26 | var counter = 1 27 | val renderLabel: js.Function1[ /* event */ JQueryEventObject, scala.Unit] = eo => 28 | counter += 1 29 | $[Label]("#label").text(s"Value is $counter") 30 | 31 | $[Button]("#button") 32 | .text("bumpit") 33 | .on("click", renderLabel) 34 | 35 | $("#accordion").accordion() 36 | end main 37 | -------------------------------------------------------------------------------- /leaflet/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | leaflet demo 6 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /leaflet/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalajs.dom.{document, html} 4 | import typings.leaflet.mod as L 5 | 6 | import scala.scalajs.js 7 | 8 | val TileLayerUri = 9 | "https://tile.openstreetmap.org/{z}/{x}/{y}.png" 10 | 11 | @main 12 | def main: Unit = 13 | val el = document.getElementById("content").asInstanceOf[html.Element] 14 | val map = L.map(el).setView(L.LatLngLiteral(51.505, -0.09), zoom = 13) 15 | 16 | L.tileLayer( 17 | TileLayerUri, 18 | L.TileLayerOptions() 19 | .setId("mapbox.streets") 20 | .setMaxZoom(19) 21 | .setAttribution( 22 | """Map data © OpenStreetMap contributors, 23 | |CC-BY-SA, 24 | |Imagery © Mapbox""".stripMargin 25 | ) 26 | ).addTo(map) 27 | 28 | L.marker(L.LatLngLiteral(51.5, -0.09), L.MarkerOptions().setTitle("I am a marker")) 29 | .bindPopup("I am a popup") 30 | .addTo(map) 31 | 32 | L.circle( 33 | L.LatLngLiteral(51.508, -0.11), 34 | L.CircleMarkerOptions().setColor("red").setFillColor("#f03").setFillOpacity(0.5).setRadius(500) 35 | ).bindPopup("I am a circle") 36 | .addTo(map) 37 | 38 | L.circle( 39 | L.LatLngLiteral(51.516, -0.11), 40 | L.CircleMarkerOptions().setColor("green").setFillColor("#f03").setFillOpacity(0.5).setRadius(200) 41 | ).addTo(map) 42 | 43 | L.polygon(js.Array(L.LatLngLiteral(51.509, -0.08), L.LatLngLiteral(51.503, -0.06), L.LatLngLiteral(51.51, -0.047))) 44 | .bindPopup("I am a polygon") 45 | .addTo(map) 46 | 47 | L.popup() 48 | .setLatLng(L.LatLngLiteral(51.5, -0.09)) 49 | .setContent("I am a standalone popup.") 50 | .openOn(map) 51 | end main 52 | -------------------------------------------------------------------------------- /lodash/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import org.scalablytyped.runtime.NumberDictionary 2 | import typings.lodash.fpMod as Fp 3 | import typings.lodash.mod.{ArrayIterator, MemoListIterator, ^ as L} 4 | import typings.node.global.console 5 | import typings.std.ArrayLike 6 | 7 | import scala.scalajs.js 8 | 9 | class Person(val name: String, val age: Int) extends js.Object 10 | 11 | val Fiona = new Person("Fiona First", 1) 12 | val Sam = new Person("Sam Second", 101) 13 | val Persons = js.Array(Fiona, Sam) 14 | 15 | // matches structurally 16 | def isArrayLike[T](ts: js.Array[T]): ArrayLike[T] = 17 | ts.asInstanceOf[ArrayLike[T]] 18 | 19 | @main 20 | def main: Unit = 21 | 22 | val summarizeNames: MemoListIterator[Person, String, js.Array[Person]] = 23 | (prev, curr, idx, all) => prev + " and " + curr.name 24 | 25 | val value2 = L.reduce(Persons, summarizeNames, "") 26 | console.log("Summarized names of two persons", value2) 27 | 28 | val value3 = L.reduce(js.Array[Person](), summarizeNames, "") 29 | console.log("Summarized names of no persons", value3) 30 | 31 | val toAge: ArrayIterator[Person, Int] = 32 | (curr, _, _) => curr.age 33 | 34 | console.log("Ages for persons", L.map(Persons.asInstanceOf[NumberDictionary[Double]], toAge)) 35 | 36 | console.log("fields for Fiona", L.entriesIn(Fiona)) 37 | 38 | console.log("Dropped first", Fp.^.drop(1, isArrayLike(Persons))) 39 | end main 40 | -------------------------------------------------------------------------------- /node-express/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import typings.express.mod as e 2 | import typings.expressServeStaticCore.mod.* 3 | import typings.node.global.console 4 | import typings.node.processMod.^ as process 5 | 6 | import scala.scalajs.js 7 | 8 | object WelcomeController: 9 | 10 | val Router: Router = 11 | e.Router() 12 | 13 | val Index: RequestHandler[Unit, String, Unit, Unit, Unit] = 14 | (_, res, _) => res.send("Hello, World!") 15 | 16 | trait HasName extends js.Object: 17 | val name: js.UndefOr[String] 18 | 19 | val Name: RequestHandler[HasName, String, Unit, Unit, Unit] = (req, res, next) => 20 | res.send(s"Hello, ${req.params.name.getOrElse("No Name")}!") 21 | 22 | Router 23 | .get[Unit, String, Unit, Unit, Unit]("/", Index) 24 | .get[HasName, String, Unit, Unit, Unit]("/:name", Name) 25 | 26 | end WelcomeController 27 | 28 | @main 29 | def main: Unit = 30 | val app = e() 31 | val port = process.env.get("PORT").flatMap(_.toOption).fold(3000)(_.toInt) 32 | app.use("/welcome", WelcomeController.Router) 33 | 34 | app.listen(port, () => console.log(s"Listening at http://localhost:$port/")) 35 | end main 36 | -------------------------------------------------------------------------------- /onsenui/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Click me! 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /onsenui/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import typings.jquery.{JQueryEventObject, mod as $} 4 | import typings.onsenui.mod as ons 5 | 6 | import scala.scalajs.js 7 | 8 | @main 9 | def main: Unit = 10 | 11 | /** Note, using `on` with the jquery typings is very frustrating. It'll be fixed eventually, but for now I would 12 | * copy/paste the facade and delete a bunch of lines to make it simpler 13 | */ 14 | val f: js.Function1[JQueryEventObject, Unit] = _ => ons.notification.alert("Button is tapped!") 15 | 16 | $("ons-button").on("click", f) 17 | end main 18 | 19 | -------------------------------------------------------------------------------- /p5/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | P5 demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /p5/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import org.scalajs.dom.console 2 | import typings.p5.mod.{p5InstanceExtensions, ^ as P5} 3 | 4 | import scala.scalajs.js 5 | 6 | @main 7 | def main: Unit = 8 | val sketch = P5Facade { p => 9 | val x = 100 10 | val y = 100 11 | 12 | p.setup = () => p.createCanvas(700, 410) 13 | 14 | p.draw = () => 15 | p.background(0) 16 | p.fill(255) 17 | p.rect(x, y, 50, 50) 18 | } 19 | console.warn(sketch.windowHeight) 20 | end main 21 | 22 | 23 | object P5Facade: 24 | 25 | /** We need this because the function `sketch` provided to `P5` has not been specified well in typescript 26 | * 27 | * @param sketch 28 | * a closure that can set optional preload(), setup(), and/or draw() properties on the given p5 instance 29 | */ 30 | def apply(sketch: js.Function1[P5Config with p5InstanceExtensions, Unit]): P5 = 31 | new P5(sketch.asInstanceOf[js.Function1[Any, Any]]) 32 | 33 | /** We need this because in the `p5` trait, the functions have been translated to methods, so we can't change them 34 | */ 35 | @js.native 36 | trait P5Config extends js.Object: 37 | var draw: js.Function0[Unit] = js.native 38 | var preload: js.Function0[Unit] = js.native 39 | var remove: js.Function0[Unit] = js.native 40 | var setup: js.Function0[Unit] = js.native 41 | end P5Config 42 | end P5Facade 43 | -------------------------------------------------------------------------------- /phaser/src/main/js/gems.jsn: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "diamond_0000": 4 | { 5 | "frame": {"x":2,"y":2,"w":64,"h":64}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 9 | "sourceSize": {"w":64,"h":64}, 10 | "pivot": {"x":0.5,"y":0.5} 11 | }, 12 | "diamond_0001": 13 | { 14 | "frame": {"x":2,"y":68,"w":64,"h":64}, 15 | "rotated": false, 16 | "trimmed": false, 17 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 18 | "sourceSize": {"w":64,"h":64}, 19 | "pivot": {"x":0.5,"y":0.5} 20 | }, 21 | "diamond_0002": 22 | { 23 | "frame": {"x":2,"y":134,"w":64,"h":64}, 24 | "rotated": false, 25 | "trimmed": false, 26 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 27 | "sourceSize": {"w":64,"h":64}, 28 | "pivot": {"x":0.5,"y":0.5} 29 | }, 30 | "diamond_0003": 31 | { 32 | "frame": {"x":68,"y":2,"w":64,"h":64}, 33 | "rotated": false, 34 | "trimmed": false, 35 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 36 | "sourceSize": {"w":64,"h":64}, 37 | "pivot": {"x":0.5,"y":0.5} 38 | }, 39 | "diamond_0004": 40 | { 41 | "frame": {"x":68,"y":68,"w":64,"h":64}, 42 | "rotated": false, 43 | "trimmed": false, 44 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 45 | "sourceSize": {"w":64,"h":64}, 46 | "pivot": {"x":0.5,"y":0.5} 47 | }, 48 | "diamond_0005": 49 | { 50 | "frame": {"x":68,"y":134,"w":64,"h":64}, 51 | "rotated": false, 52 | "trimmed": false, 53 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 54 | "sourceSize": {"w":64,"h":64}, 55 | "pivot": {"x":0.5,"y":0.5} 56 | }, 57 | "diamond_0006": 58 | { 59 | "frame": {"x":134,"y":2,"w":64,"h":64}, 60 | "rotated": false, 61 | "trimmed": false, 62 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 63 | "sourceSize": {"w":64,"h":64}, 64 | "pivot": {"x":0.5,"y":0.5} 65 | }, 66 | "diamond_0007": 67 | { 68 | "frame": {"x":134,"y":68,"w":64,"h":64}, 69 | "rotated": false, 70 | "trimmed": false, 71 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 72 | "sourceSize": {"w":64,"h":64}, 73 | "pivot": {"x":0.5,"y":0.5} 74 | }, 75 | "diamond_0008": 76 | { 77 | "frame": {"x":134,"y":134,"w":64,"h":64}, 78 | "rotated": false, 79 | "trimmed": false, 80 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 81 | "sourceSize": {"w":64,"h":64}, 82 | "pivot": {"x":0.5,"y":0.5} 83 | }, 84 | "diamond_0009": 85 | { 86 | "frame": {"x":200,"y":2,"w":64,"h":64}, 87 | "rotated": false, 88 | "trimmed": false, 89 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 90 | "sourceSize": {"w":64,"h":64}, 91 | "pivot": {"x":0.5,"y":0.5} 92 | }, 93 | "diamond_0010": 94 | { 95 | "frame": {"x":200,"y":68,"w":64,"h":64}, 96 | "rotated": false, 97 | "trimmed": false, 98 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 99 | "sourceSize": {"w":64,"h":64}, 100 | "pivot": {"x":0.5,"y":0.5} 101 | }, 102 | "diamond_0011": 103 | { 104 | "frame": {"x":200,"y":134,"w":64,"h":64}, 105 | "rotated": false, 106 | "trimmed": false, 107 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 108 | "sourceSize": {"w":64,"h":64}, 109 | "pivot": {"x":0.5,"y":0.5} 110 | }, 111 | "diamond_0012": 112 | { 113 | "frame": {"x":266,"y":2,"w":64,"h":64}, 114 | "rotated": false, 115 | "trimmed": false, 116 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 117 | "sourceSize": {"w":64,"h":64}, 118 | "pivot": {"x":0.5,"y":0.5} 119 | }, 120 | "diamond_0013": 121 | { 122 | "frame": {"x":266,"y":68,"w":64,"h":64}, 123 | "rotated": false, 124 | "trimmed": false, 125 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 126 | "sourceSize": {"w":64,"h":64}, 127 | "pivot": {"x":0.5,"y":0.5} 128 | }, 129 | "diamond_0014": 130 | { 131 | "frame": {"x":266,"y":134,"w":64,"h":64}, 132 | "rotated": false, 133 | "trimmed": false, 134 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 135 | "sourceSize": {"w":64,"h":64}, 136 | "pivot": {"x":0.5,"y":0.5} 137 | }, 138 | "diamond_0015": 139 | { 140 | "frame": {"x":332,"y":2,"w":64,"h":64}, 141 | "rotated": false, 142 | "trimmed": false, 143 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 144 | "sourceSize": {"w":64,"h":64}, 145 | "pivot": {"x":0.5,"y":0.5} 146 | }, 147 | "prism_0000": 148 | { 149 | "frame": {"x":332,"y":68,"w":64,"h":64}, 150 | "rotated": false, 151 | "trimmed": false, 152 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 153 | "sourceSize": {"w":64,"h":64}, 154 | "pivot": {"x":0.5,"y":0.5} 155 | }, 156 | "prism_0001": 157 | { 158 | "frame": {"x":332,"y":134,"w":64,"h":64}, 159 | "rotated": false, 160 | "trimmed": false, 161 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 162 | "sourceSize": {"w":64,"h":64}, 163 | "pivot": {"x":0.5,"y":0.5} 164 | }, 165 | "prism_0002": 166 | { 167 | "frame": {"x":398,"y":2,"w":64,"h":64}, 168 | "rotated": false, 169 | "trimmed": false, 170 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 171 | "sourceSize": {"w":64,"h":64}, 172 | "pivot": {"x":0.5,"y":0.5} 173 | }, 174 | "prism_0003": 175 | { 176 | "frame": {"x":398,"y":68,"w":64,"h":64}, 177 | "rotated": false, 178 | "trimmed": false, 179 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 180 | "sourceSize": {"w":64,"h":64}, 181 | "pivot": {"x":0.5,"y":0.5} 182 | }, 183 | "prism_0004": 184 | { 185 | "frame": {"x":398,"y":134,"w":64,"h":64}, 186 | "rotated": false, 187 | "trimmed": false, 188 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 189 | "sourceSize": {"w":64,"h":64}, 190 | "pivot": {"x":0.5,"y":0.5} 191 | }, 192 | "prism_0005": 193 | { 194 | "frame": {"x":464,"y":2,"w":64,"h":64}, 195 | "rotated": false, 196 | "trimmed": false, 197 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 198 | "sourceSize": {"w":64,"h":64}, 199 | "pivot": {"x":0.5,"y":0.5} 200 | }, 201 | "prism_0006": 202 | { 203 | "frame": {"x":464,"y":68,"w":64,"h":64}, 204 | "rotated": false, 205 | "trimmed": false, 206 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 207 | "sourceSize": {"w":64,"h":64}, 208 | "pivot": {"x":0.5,"y":0.5} 209 | }, 210 | "ruby_0000": 211 | { 212 | "frame": {"x":464,"y":134,"w":64,"h":64}, 213 | "rotated": false, 214 | "trimmed": false, 215 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 216 | "sourceSize": {"w":64,"h":64}, 217 | "pivot": {"x":0.5,"y":0.5} 218 | }, 219 | "ruby_0001": 220 | { 221 | "frame": {"x":530,"y":2,"w":64,"h":64}, 222 | "rotated": false, 223 | "trimmed": false, 224 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 225 | "sourceSize": {"w":64,"h":64}, 226 | "pivot": {"x":0.5,"y":0.5} 227 | }, 228 | "ruby_0002": 229 | { 230 | "frame": {"x":530,"y":68,"w":64,"h":64}, 231 | "rotated": false, 232 | "trimmed": false, 233 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 234 | "sourceSize": {"w":64,"h":64}, 235 | "pivot": {"x":0.5,"y":0.5} 236 | }, 237 | "ruby_0003": 238 | { 239 | "frame": {"x":530,"y":134,"w":64,"h":64}, 240 | "rotated": false, 241 | "trimmed": false, 242 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 243 | "sourceSize": {"w":64,"h":64}, 244 | "pivot": {"x":0.5,"y":0.5} 245 | }, 246 | "ruby_0004": 247 | { 248 | "frame": {"x":596,"y":2,"w":64,"h":64}, 249 | "rotated": false, 250 | "trimmed": false, 251 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 252 | "sourceSize": {"w":64,"h":64}, 253 | "pivot": {"x":0.5,"y":0.5} 254 | }, 255 | "ruby_0005": 256 | { 257 | "frame": {"x":596,"y":68,"w":64,"h":64}, 258 | "rotated": false, 259 | "trimmed": false, 260 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 261 | "sourceSize": {"w":64,"h":64}, 262 | "pivot": {"x":0.5,"y":0.5} 263 | }, 264 | "ruby_0006": 265 | { 266 | "frame": {"x":596,"y":134,"w":64,"h":64}, 267 | "rotated": false, 268 | "trimmed": false, 269 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 270 | "sourceSize": {"w":64,"h":64}, 271 | "pivot": {"x":0.5,"y":0.5} 272 | }, 273 | "square_0000": 274 | { 275 | "frame": {"x":662,"y":2,"w":64,"h":64}, 276 | "rotated": false, 277 | "trimmed": false, 278 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 279 | "sourceSize": {"w":64,"h":64}, 280 | "pivot": {"x":0.5,"y":0.5} 281 | }, 282 | "square_0001": 283 | { 284 | "frame": {"x":662,"y":68,"w":64,"h":64}, 285 | "rotated": false, 286 | "trimmed": false, 287 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 288 | "sourceSize": {"w":64,"h":64}, 289 | "pivot": {"x":0.5,"y":0.5} 290 | }, 291 | "square_0002": 292 | { 293 | "frame": {"x":662,"y":134,"w":64,"h":64}, 294 | "rotated": false, 295 | "trimmed": false, 296 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 297 | "sourceSize": {"w":64,"h":64}, 298 | "pivot": {"x":0.5,"y":0.5} 299 | }, 300 | "square_0003": 301 | { 302 | "frame": {"x":728,"y":2,"w":64,"h":64}, 303 | "rotated": false, 304 | "trimmed": false, 305 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 306 | "sourceSize": {"w":64,"h":64}, 307 | "pivot": {"x":0.5,"y":0.5} 308 | }, 309 | "square_0004": 310 | { 311 | "frame": {"x":728,"y":68,"w":64,"h":64}, 312 | "rotated": false, 313 | "trimmed": false, 314 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 315 | "sourceSize": {"w":64,"h":64}, 316 | "pivot": {"x":0.5,"y":0.5} 317 | }, 318 | "square_0005": 319 | { 320 | "frame": {"x":728,"y":134,"w":64,"h":64}, 321 | "rotated": false, 322 | "trimmed": false, 323 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 324 | "sourceSize": {"w":64,"h":64}, 325 | "pivot": {"x":0.5,"y":0.5} 326 | }, 327 | "square_0006": 328 | { 329 | "frame": {"x":794,"y":2,"w":64,"h":64}, 330 | "rotated": false, 331 | "trimmed": false, 332 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 333 | "sourceSize": {"w":64,"h":64}, 334 | "pivot": {"x":0.5,"y":0.5} 335 | }, 336 | "square_0007": 337 | { 338 | "frame": {"x":860,"y":2,"w":64,"h":64}, 339 | "rotated": false, 340 | "trimmed": false, 341 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 342 | "sourceSize": {"w":64,"h":64}, 343 | "pivot": {"x":0.5,"y":0.5} 344 | }, 345 | "square_0008": 346 | { 347 | "frame": {"x":926,"y":2,"w":64,"h":64}, 348 | "rotated": false, 349 | "trimmed": false, 350 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 351 | "sourceSize": {"w":64,"h":64}, 352 | "pivot": {"x":0.5,"y":0.5} 353 | }, 354 | "square_0009": 355 | { 356 | "frame": {"x":794,"y":68,"w":64,"h":64}, 357 | "rotated": false, 358 | "trimmed": false, 359 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 360 | "sourceSize": {"w":64,"h":64}, 361 | "pivot": {"x":0.5,"y":0.5} 362 | }, 363 | "square_0010": 364 | { 365 | "frame": {"x":794,"y":134,"w":64,"h":64}, 366 | "rotated": false, 367 | "trimmed": false, 368 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 369 | "sourceSize": {"w":64,"h":64}, 370 | "pivot": {"x":0.5,"y":0.5} 371 | }, 372 | "square_0011": 373 | { 374 | "frame": {"x":860,"y":68,"w":64,"h":64}, 375 | "rotated": false, 376 | "trimmed": false, 377 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 378 | "sourceSize": {"w":64,"h":64}, 379 | "pivot": {"x":0.5,"y":0.5} 380 | }, 381 | "square_0012": 382 | { 383 | "frame": {"x":926,"y":68,"w":64,"h":64}, 384 | "rotated": false, 385 | "trimmed": false, 386 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 387 | "sourceSize": {"w":64,"h":64}, 388 | "pivot": {"x":0.5,"y":0.5} 389 | }, 390 | "square_0013": 391 | { 392 | "frame": {"x":860,"y":134,"w":64,"h":64}, 393 | "rotated": false, 394 | "trimmed": false, 395 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 396 | "sourceSize": {"w":64,"h":64}, 397 | "pivot": {"x":0.5,"y":0.5} 398 | }, 399 | "square_0014": 400 | { 401 | "frame": {"x":926,"y":134,"w":64,"h":64}, 402 | "rotated": false, 403 | "trimmed": false, 404 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 405 | "sourceSize": {"w":64,"h":64}, 406 | "pivot": {"x":0.5,"y":0.5} 407 | }}, 408 | "meta": { 409 | "app": "http://www.codeandweb.com/texturepacker", 410 | "version": "1.0", 411 | "image": "gems.png", 412 | "format": "RGBA8888", 413 | "size": {"w":992,"h":200}, 414 | "scale": "1", 415 | "smartupdate": "$TexturePacker:SmartUpdate:5dfda9c839d3ca51c00faf9458ba1fca:0b0be3727c36efcd76ae8acf6abbf211:81fc68276d1596f8f7ad75d59a9ce9b5$" 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /phaser/src/main/js/gems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/phaser/src/main/js/gems.png -------------------------------------------------------------------------------- /phaser/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | phaser demo 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /phaser/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalablytyped.runtime.{StringDictionary, TopLevel} 4 | import typings.phaser.Phaser.Types.Animations.{Animation, GenerateFrameNames} 5 | import typings.phaser.Phaser.Types.Core.GameConfig 6 | import typings.phaser.Phaser.Types.GameObjects.GameObjectConfig 7 | import typings.phaser.Phaser.Types.GameObjects.Sprite.SpriteConfig 8 | import typings.phaser.Phaser.Types.Scenes.CreateSceneFromObjectConfig 9 | import typings.phaser.mod.{Game, Scene} 10 | import typings.phaser.mod as Phaser 11 | 12 | import scala.scalajs.js 13 | import scala.scalajs.js.annotation.JSImport 14 | import scala.util.Random 15 | 16 | @JSImport("./gems.png", JSImport.Default) 17 | @js.native 18 | val GemsPng: String = js.native 19 | 20 | /* file renamed from .json because of https://github.com/webpack/webpack/issues/6586 */ 21 | @JSImport("./gems.jsn", JSImport.Default) 22 | @js.native 23 | val GemsJson: String = js.native 24 | 25 | val preload: js.ThisFunction0[Scene, Unit] = 26 | _.load.atlas("gems", GemsPng, GemsJson, js.undefined, js.undefined) 27 | 28 | val create: js.ThisFunction1[Scene, js.Object, Unit] = (scene, data) => 29 | // Define the animations first 30 | scene.anims.create( 31 | Animation() 32 | .setKey("ruby") 33 | .setFrames( 34 | scene.anims.generateFrameNames("gems", GenerateFrameNames().setPrefix("ruby_").setEnd(6).setZeroPad(4)) 35 | ) 36 | .setRepeat(-1) 37 | ); 38 | 39 | scene.anims.create( 40 | Animation() 41 | .setKey("square") 42 | .setFrames( 43 | scene.anims.generateFrameNames("gems", GenerateFrameNames().setPrefix("square_").setEnd(14).setZeroPad(4)) 44 | ) 45 | .setRepeat(-1) 46 | ) 47 | 48 | // Make 16 sprites using the config above 49 | 0 to 16 foreach { _ => 50 | // The Sprite config 51 | val config = SpriteConfig() 52 | .setKey("gems") 53 | .setX(Random.nextInt(800)) 54 | .setY(Random.nextInt(300)) 55 | .setScale(Random.between(0.5, 1.5)) 56 | /* add a member describing the annotation which is completely unchecked in typescript */ 57 | .set("anims", "ruby") 58 | scene.make.sprite(config) 59 | } 60 | 61 | // Make 16 sprites using the config above 62 | 0 to 16 foreach { _ => 63 | // A more complex animation config object. 64 | // This time with a call to delayedPlay that's a function. 65 | val config = SpriteConfig() 66 | .setKey("gems") 67 | .setX(Random.nextInt(800)) 68 | .setY(Random.nextInt(300) + 300) 69 | .setScale(Random.between(0.5, 1.5)) 70 | .set( 71 | "anims", 72 | StringDictionary[js.Any]( 73 | "key" -> "square", 74 | "repeat" -> -1, 75 | "repeatDelay" -> (1 + Random.nextInt(3)), 76 | "delayedPlay" -> (() => Math.random() * 6) 77 | ) 78 | ) 79 | scene.make.sprite(config); 80 | } 81 | 82 | val config = GameConfig() 83 | .setType(Phaser.AUTO: Double) 84 | .setParent("phaser-example") 85 | .setWidth(800) 86 | .setHeight(600) 87 | .setScene(createScene(preload = preload, create = create)) 88 | 89 | @main 90 | def main: Unit = 91 | new Game(config) 92 | 93 | /* note that we could probably have refactored this whole thing to use classes to not avoid this. 94 | * To keep in line with the example I changed CreateSceneFromObjectConfig to take `ThisFunction`s. 95 | * 96 | * The only reason it's not already there is that it's not specified in typescript, 97 | * leaving it completely unchecked. 98 | * 99 | * Unfortunately `js.ThisFunctionN` is not a subtype of the corresponding `js.FunctionN` 100 | */ 101 | @scala.inline 102 | def createScene( 103 | create: /* data */ js.ThisFunction1[Scene, js.Object, Unit] = null, 104 | extend: js.Any = null, 105 | extendDotdata: js.Any = null, 106 | init: /* data */ js.ThisFunction1[Scene, js.Object, Unit] = null, 107 | preload: js.ThisFunction0[Scene, Unit] = null, 108 | update: js.ThisFunction0[Scene, Unit] = null 109 | ): CreateSceneFromObjectConfig = 110 | val __obj = js.Dynamic.literal() 111 | if create != null then __obj.updateDynamic("create")(create) 112 | if extend != null then __obj.updateDynamic("extend")(extend.asInstanceOf[js.Any]) 113 | if extendDotdata != null then __obj.updateDynamic("extend.data")(extendDotdata.asInstanceOf[js.Any]) 114 | if init != null then __obj.updateDynamic("init")(init) 115 | if preload != null then __obj.updateDynamic("preload")(preload) 116 | if update != null then __obj.updateDynamic("update")(update.asInstanceOf[js.Any]) 117 | __obj.asInstanceOf[CreateSceneFromObjectConfig] 118 | end createScene 119 | -------------------------------------------------------------------------------- /pixi/src/main/js/a11y-light.css: -------------------------------------------------------------------------------- 1 | /* a11y-light theme */ 2 | /* Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css */ 3 | /* @author: ericwbailey */ 4 | 5 | /* Comment */ 6 | .hljs-comment, 7 | .hljs-quote { 8 | color: #696969; 9 | } 10 | 11 | /* Red */ 12 | .hljs-variable, 13 | .hljs-template-variable, 14 | .hljs-tag, 15 | .hljs-name, 16 | .hljs-selector-id, 17 | .hljs-selector-class, 18 | .hljs-regexp, 19 | .hljs-deletion { 20 | color: #d91e18; 21 | } 22 | 23 | /* Orange */ 24 | .hljs-number, 25 | .hljs-built_in, 26 | .hljs-builtin-name, 27 | .hljs-literal, 28 | .hljs-type, 29 | .hljs-params, 30 | .hljs-meta, 31 | .hljs-link { 32 | color: #aa5d00; 33 | } 34 | 35 | /* Yellow */ 36 | .hljs-attribute { 37 | color: #aa5d00; 38 | } 39 | 40 | /* Green */ 41 | .hljs-string, 42 | .hljs-symbol, 43 | .hljs-bullet, 44 | .hljs-addition { 45 | color: #008000; 46 | } 47 | 48 | /* Blue */ 49 | .hljs-title, 50 | .hljs-section { 51 | color: #007faa; 52 | } 53 | 54 | /* Purple */ 55 | .hljs-keyword, 56 | .hljs-selector-tag { 57 | color: #7928a1; 58 | } 59 | 60 | .hljs { 61 | display: block; 62 | overflow-x: auto; 63 | background: #fefefe; 64 | color: #545454; 65 | padding: 0.5em; 66 | } 67 | 68 | .hljs-emphasis { 69 | font-style: italic; 70 | } 71 | 72 | .hljs-strong { 73 | font-weight: bold; 74 | } 75 | 76 | @media screen and (-ms-high-contrast: active) { 77 | .hljs-addition, 78 | .hljs-attribute, 79 | .hljs-built_in, 80 | .hljs-builtin-name, 81 | .hljs-bullet, 82 | .hljs-comment, 83 | .hljs-link, 84 | .hljs-literal, 85 | .hljs-meta, 86 | .hljs-number, 87 | .hljs-params, 88 | .hljs-string, 89 | .hljs-symbol, 90 | .hljs-type, 91 | .hljs-quote { 92 | color: highlight; 93 | } 94 | 95 | .hljs-keyword, 96 | .hljs-selector-tag { 97 | font-weight: bold; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /pixi/src/main/js/assets/bg_grass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/bg_grass.jpg -------------------------------------------------------------------------------- /pixi/src/main/js/assets/bg_scene_rotate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/bg_scene_rotate.jpg -------------------------------------------------------------------------------- /pixi/src/main/js/assets/bunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/bunny.png -------------------------------------------------------------------------------- /pixi/src/main/js/assets/eggHead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/eggHead.png -------------------------------------------------------------------------------- /pixi/src/main/js/assets/p2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/p2.jpeg -------------------------------------------------------------------------------- /pixi/src/main/js/assets/pixi-filters/displacement_map_repeat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/pixi-filters/displacement_map_repeat.jpg -------------------------------------------------------------------------------- /pixi/src/main/js/assets/pixi-filters/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/pixi-filters/flag.png -------------------------------------------------------------------------------- /pixi/src/main/js/assets/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/star.png -------------------------------------------------------------------------------- /pixi/src/main/js/assets/trail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/trail.png -------------------------------------------------------------------------------- /pixi/src/main/js/assets/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/pixi/src/main/js/assets/video.mp4 -------------------------------------------------------------------------------- /pixi/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixi.js with Scala.js 7 | 8 | 9 | 12 | 13 |
    14 |

    Pixi.js with Scala.js!

    15 | 16 |

    17 | Click in the menu on the left to see the examples from the official website translated into 18 | Scala, using Scala.js and ScalablyTyped. 19 |

    20 |

    21 | This project uses version 5 of Pixi. 22 |

    23 |
    24 | 25 |
    26 |

    27 |
    28 |

    Source code

    29 |
    30 | See PIXI JavaScript example 31 |
    32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pixi/src/main/js/styles.css: -------------------------------------------------------------------------------- 1 | #menu { 2 | position: fixed; 3 | height: 100%; 4 | top: 0px; 5 | left: 0px; 6 | background-color: #000099; 7 | color: white; 8 | width: 200px; 9 | padding: 10px; 10 | overflow-y: auto; 11 | } 12 | 13 | .menu-section { 14 | border-bottom: 1px solid white; 15 | margin-bottom: 10px; 16 | } 17 | 18 | .section-header { 19 | font-weight: bold; 20 | font-size: 16; 21 | } 22 | 23 | .example-option { 24 | padding: 5px; 25 | cursor: pointer; 26 | } 27 | 28 | pre code { 29 | border: 1px solid black; 30 | } 31 | 32 | #code-container { 33 | width: 800px; 34 | } 35 | 36 | #overall-container { 37 | margin-left: 250px; 38 | display: none 39 | } 40 | 41 | #welcome-message { 42 | margin-left: 250px; 43 | } 44 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/assets.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalablytyped.runtime.TopLevel 4 | 5 | import scala.scalajs.js 6 | import scala.scalajs.js.annotation.JSImport 7 | 8 | object assets: 9 | object pixiFilters: 10 | @js.native 11 | @JSImport("./assets/pixi-filters/displacement_map_repeat.jpg", JSImport.Default) 12 | object DisplacementMapRepeat extends TopLevel[String] 13 | 14 | @js.native 15 | @JSImport("./assets/pixi-filters/flag.png", JSImport.Default) 16 | object FlagImage extends TopLevel[String] 17 | 18 | @js.native 19 | @JSImport("./assets/bg_grass.jpg", JSImport.Default) 20 | object BackgroundGrass extends TopLevel[String] 21 | 22 | @js.native 23 | @JSImport("./assets/bg_scene_rotate.jpg", JSImport.Default) 24 | object BackgroundSceneRotate extends TopLevel[String] 25 | 26 | @js.native 27 | @JSImport("./assets/bunny.png", JSImport.Default) 28 | object BunnyImage extends TopLevel[String] 29 | 30 | @js.native 31 | @JSImport("./assets/eggHead.png", JSImport.Default) 32 | object EggHeadImage extends TopLevel[String] 33 | 34 | @js.native 35 | @JSImport("./assets/p2.jpeg", JSImport.Default) 36 | object P2Image extends TopLevel[String] 37 | 38 | @js.native 39 | @JSImport("./assets/star.png", JSImport.Default) 40 | object StarImage extends TopLevel[String] 41 | 42 | @js.native 43 | @JSImport("./assets/video.mp4", JSImport.Default) 44 | object TheVideo extends TopLevel[String] 45 | 46 | @js.native 47 | @JSImport("./assets/trail.png", JSImport.Default) 48 | object TrailImage extends TopLevel[String] 49 | end assets 50 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/demosadvanced/MouseTrail.scala: -------------------------------------------------------------------------------- 1 | package demo.demosadvanced 2 | 3 | import demo.assets.TrailImage 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.mod.interaction.InteractionManager 6 | import typings.pixiJs.mod.{Application, BLEND_MODES, Point, SimpleRope, Texture} 7 | import demo.monkeypatching.PIXIPatching.* 8 | import typings.pixiJs.anon.Antialias as ApplicationOptions 9 | 10 | import scala.collection.mutable 11 | import scala.scalajs.js 12 | import scala.scalajs.js.JSConverters.* 13 | 14 | case object MouseTrail extends PIXIExample: 15 | 16 | val name: String = "Mouse trail" 17 | 18 | val pixiUrl: String = 19 | "https://pixijs.io/examples/#/demos-advanced/mouse-trail.js" 20 | 21 | def newApplication(): Application = 22 | val app = new Application(ApplicationOptions().setBackgroundColor(0x1099bb)) 23 | 24 | //Get the texture for rope. 25 | val trailTexture = Texture.from(TrailImage) 26 | val historyX = mutable.Queue[Double]() 27 | val historyY = mutable.Queue[Double]() 28 | //historySize determines how long the trail will be. 29 | val historySize = 20 30 | //ropeSize determines how smooth the trail will be. 31 | val ropeSize = 100 32 | 33 | //Create history array. 34 | for _ <- 0 until historySize do 35 | historyX += 0 36 | historyY += 0 37 | 38 | //Create rope points. 39 | val points: Vector[typings.pixiJs.PIXI.Point] = 40 | (for (_ <- 0 until ropeSize) yield new Point(0, 0)).toVector // sadly js.Array is invariant 41 | 42 | //Create the rope 43 | val rope = new SimpleRope(trailTexture, points.toJSArray) 44 | 45 | //Set the blendmode 46 | rope.blendMode = BLEND_MODES.ADD 47 | 48 | app.stage.addChild(rope) 49 | 50 | def ticker(): Unit = 51 | // Read mouse points, this could be done also in mousemove/touchmove update. 52 | // For simplicity it is done here for now. 53 | // When implementing this properly, make sure to implement touchmove as interaction plugins mouse might not update 54 | // on certain devices. 55 | val mousePosition = 56 | app.renderer.plugins 57 | .asInstanceOf[js.Dynamic] 58 | .interaction 59 | .asInstanceOf[InteractionManager] 60 | .mouse 61 | .global 62 | 63 | //Update the mouse values to history 64 | historyX.dequeue() 65 | historyX += mousePosition.x 66 | historyY.dequeue() 67 | historyY += mousePosition.y 68 | //Update the points to correspond with history. 69 | for i <- 0 until ropeSize do 70 | val p = points(i) 71 | 72 | //Smooth the curve with cubic interpolation to prevent sharp edges. 73 | val ix = 74 | cubicInterpolation( 75 | historyX.toList, 76 | i.toDouble / ropeSize * historySize 77 | ) 78 | val iy = 79 | cubicInterpolation( 80 | historyY.toList, 81 | i.toDouble / ropeSize * historySize 82 | ) 83 | 84 | p.x = ix 85 | p.y = iy 86 | end for 87 | end ticker 88 | 89 | // Listen for animate update 90 | app.ticker.add(() => ticker()) 91 | 92 | /** Cubic interpolation based on https://github.com/osuushi/Smooth.js 93 | */ 94 | def clipInput[T](k: Int, arr: Seq[T]): T = 95 | arr(math.max(0, math.min(arr.size - 1, k))) 96 | 97 | def getTangent(k: Int, factor: Double, array: Seq[Double]): Double = 98 | factor * (clipInput(k + 1, array) - clipInput(k - 1, array)) / 2 99 | 100 | def cubicInterpolation(array: Seq[Double], t: Double, tangentFactor: Double = 1): Double = 101 | 102 | val k = Math.floor(t).toInt 103 | val m = Seq( 104 | getTangent(k, tangentFactor, array), 105 | getTangent(k + 1, tangentFactor, array) 106 | ) 107 | val p = Seq(clipInput(k, array), clipInput(k + 1, array)) 108 | val u = t - k 109 | val t2 = u * u 110 | val t3 = u * t2 111 | (2 * t3 - 3 * t2 + 1) * p.head + (t3 - 2 * t2 + u) * m.head + (-2 * t3 + 3 * t2) * p( 112 | 1 113 | ) + (t3 - t2) * m(1) 114 | end cubicInterpolation 115 | 116 | app 117 | end newApplication 118 | end MouseTrail 119 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/demosadvanced/StarWarp.scala: -------------------------------------------------------------------------------- 1 | package demo.demosadvanced 2 | 3 | import demo.assets.StarImage 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.anon.Antialias as ApplicationOptions 6 | import typings.pixiJs.mod.{Application, Sprite, Texture} 7 | import demo.monkeypatching.PIXIPatching.* 8 | 9 | import scala.scalajs.js.timers.setInterval 10 | 11 | case object StarWarp extends PIXIExample: 12 | 13 | val name: String = "Star warp" 14 | 15 | val pixiUrl: String = 16 | "https://pixijs.io/examples/#/demos-advanced/star-warp.js" 17 | 18 | def newApplication(): Application = 19 | val app = new Application(ApplicationOptions().setBackgroundColor(0)) 20 | 21 | //Get the texture for rope. 22 | val starTexture = Texture.from(StarImage) 23 | 24 | val starAmount = 1000 25 | var cameraZ = 0.0 26 | val fov = 20.0 27 | val baseSpeed = 0.025 28 | var speed = 0.0 29 | var warpSpeed = 0.0 30 | val starStretch = 5.0 31 | val starBaseSize = 0.05 32 | 33 | final class Star: 34 | val sprite: Sprite = new Sprite(starTexture) 35 | var z: Double = 0 36 | var x: Double = 0 37 | var y: Double = 0 38 | 39 | def randomizeStar(star: Star, initial: Boolean): Unit = 40 | star.z = 41 | if initial then Math.random() * 2000 42 | else cameraZ + Math.random() * 1000 + 2000 43 | 44 | //Calculate star positions with radial random coordinate so no star hits the camera. 45 | val deg = Math.random() * Math.PI * 2 46 | val distance = Math.random() * 50 + 1 47 | star.x = Math.cos(deg) * distance 48 | star.y = Math.sin(deg) * distance 49 | end randomizeStar 50 | 51 | //Create the stars 52 | val stars = for (_ <- 0 until starAmount) yield 53 | val star = new Star 54 | star.sprite.anchor.x = 0.5 55 | star.sprite.anchor.y = 0.7 56 | randomizeStar(star, initial = true) 57 | app.stage.addChild(star.sprite) 58 | star 59 | 60 | //Change flight speed every 5 seconds 61 | setInterval(5000) { 62 | warpSpeed = 1 - warpSpeed 63 | } 64 | 65 | def ticker(delta: Double): Unit = 66 | //Simple easing. This should be changed to proper easing function when used for real. 67 | speed += (warpSpeed - speed) / 20 68 | cameraZ += delta * 10 * (speed + baseSpeed) 69 | for star <- stars do 70 | if star.z < cameraZ then randomizeStar(star, initial = false) 71 | 72 | //Map star 3d position to 2d with really simple projection 73 | val z = star.z - cameraZ 74 | star.sprite.x = star.x * (fov / z) * app.renderer.screen.width + app.renderer.screen.width / 2 75 | star.sprite.y = star.y * (fov / z) * app.renderer.screen.width + app.renderer.screen.height / 2 76 | 77 | //Calculate star scale & rotation. 78 | val dxCenter = star.sprite.x - app.renderer.screen.width / 2 79 | val dyCenter = star.sprite.y - app.renderer.screen.height / 2 80 | val distanceCenter = 81 | Math.sqrt(dxCenter * dxCenter + dyCenter + dyCenter) 82 | val distanceScale = Math.max(0, (2000 - z) / 2000) 83 | star.sprite.scale.x = distanceScale * starBaseSize 84 | //Star is looking towards center so that y axis is towards center. 85 | //Scale the star depending on how fast we are moving, what the stretchfactor is and depending on how far away 86 | // it is from the center. 87 | star.sprite.scale.y = distanceScale * starBaseSize + 88 | distanceScale * speed * starStretch * distanceCenter / app.renderer.screen.width 89 | star.sprite.rotation = Math.atan2(dyCenter, dxCenter) + Math.PI / 2 90 | end for 91 | end ticker 92 | 93 | // Listen for animate update 94 | app.ticker.add(ticker(_: Double)) 95 | 96 | app 97 | end newApplication 98 | end StarWarp 99 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/demosbasic/Basics.scala: -------------------------------------------------------------------------------- 1 | package demo.demosbasic 2 | 3 | import demo.assets.BunnyImage 4 | import demo.pixi.PIXIExample 5 | import demo.monkeypatching.PIXIPatching.* 6 | import typings.pixiJs.anon.Antialias as ApplicationOptions 7 | import typings.pixiJs.mod.{Application, Sprite, Texture} 8 | 9 | case object Basics extends PIXIExample: 10 | 11 | val name: String = "Basics" 12 | 13 | val pixiUrl: String = "https://pixijs.io/examples/#/sprite/basic.js" 14 | 15 | def newApplication(): Application = 16 | val app = new Application(ApplicationOptions().setBackgroundColor(0x1099bb)) 17 | 18 | val texture = Texture.from(BunnyImage) 19 | 20 | // create a new Sprite from an image path 21 | val bunny: Sprite = new Sprite(texture) 22 | 23 | // center the sprite's anchor point 24 | bunny.anchor.set(0.5) 25 | 26 | // move the sprite to the center of the screen 27 | bunny.x = app.screen.width / 2 28 | bunny.y = app.screen.height / 2 29 | 30 | app.stage.addChild(bunny) 31 | 32 | val ticker = (delta: Double) => 33 | // just for fun, let's rotate mr rabbit a little 34 | // delta is 1 if running at 100% performance 35 | // creates frame-independent transformation 36 | bunny.rotation += 0.1 * delta 37 | 38 | // Listen for animate update 39 | app.ticker.add(ticker) 40 | 41 | app 42 | end newApplication 43 | end Basics 44 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/demosbasic/ContainerPivot.scala: -------------------------------------------------------------------------------- 1 | package demo.demosbasic 2 | 3 | import demo.assets.BunnyImage 4 | import demo.monkeypatching.PIXIPatching.* 5 | import demo.pixi.PIXIExample 6 | import typings.pixiJs.anon.Antialias as ApplicationOptions 7 | import typings.pixiJs.mod.{Application, Container, Sprite, Texture} 8 | 9 | case object ContainerPivot extends PIXIExample: 10 | 11 | val name: String = "Container Pivot" 12 | 13 | val pixiUrl: String = "https://pixijs.io/examples/#/demos-basic/container.js" 14 | 15 | def newApplication(): Application = 16 | val app = new Application(ApplicationOptions().setBackgroundColor(0x1099bb)) 17 | 18 | val container = new Container() 19 | 20 | app.stage.addChild(container) 21 | 22 | // Create a new texture 23 | val texture = Texture.from(BunnyImage) 24 | 25 | // Create a 5x5 grid of bunnies 26 | for i <- 0 until 25 do 27 | val bunny = new Sprite(texture) 28 | bunny.anchor.set(0.5) 29 | bunny.x = (i % 5) * 40 30 | bunny.y = i / 5 * 40 31 | container.addChild(bunny) 32 | 33 | // Move container to the center 34 | container.x = app.screen.width / 2 35 | container.y = app.screen.height / 2 36 | 37 | // Center bunny sprite in local container coordinates 38 | container.pivot.x = container.width / 2 39 | container.pivot.y = container.height / 2 40 | 41 | val tickerF = (delta: Double) => 42 | // rotate the container! 43 | // use delta to create frame-independent transform 44 | container.rotation -= 0.01 * delta 45 | 46 | // Listen for animate update 47 | app.ticker.add(tickerF) 48 | 49 | app 50 | end newApplication 51 | end ContainerPivot 52 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/demosbasic/Containers.scala: -------------------------------------------------------------------------------- 1 | package demo.demosbasic 2 | 3 | import demo.assets.BunnyImage 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.anon.Antialias as ApplicationOptions 6 | import typings.pixiJs.mod.{Application, Container, Sprite, Texture} 7 | 8 | case object Containers extends PIXIExample: 9 | 10 | val name: String = "Container" 11 | 12 | val pixiUrl: String = "https://pixijs.io/examples/#/basics/container.js" 13 | 14 | def newApplication(): Application = 15 | 16 | val app = new Application(ApplicationOptions().setBackgroundColor(0x1099bb)) 17 | 18 | val container = new Container() 19 | 20 | app.stage.addChild(container) 21 | 22 | val texture = Texture.from(BunnyImage) 23 | 24 | // Create a 5x5 grid of bunnies 25 | for i <- 0 until 25 do 26 | val bunny = new Sprite(texture) 27 | bunny.anchor.set(0.5) 28 | bunny.x = (i % 5) * 40 29 | bunny.y = i / 5 * 40 30 | container.addChild(bunny) 31 | 32 | // Center on the screen 33 | container.x = (app.screen.width - container.width) / 2 34 | container.y = (app.screen.height - container.height) / 2 35 | 36 | app 37 | end newApplication 38 | end Containers 39 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/demosbasic/Tinting.scala: -------------------------------------------------------------------------------- 1 | package demo.demosbasic 2 | 3 | import demo.assets.EggHeadImage 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.mod.{Application, Rectangle, Sprite, Texture} 6 | 7 | import demo.monkeypatching.PIXIPatching.* 8 | 9 | case object Tinting extends PIXIExample: 10 | 11 | val name: String = "Tinting" 12 | 13 | val pixiUrl: String = "https://pixijs.io/examples/#/demos-basic/tinting.js" 14 | 15 | def newApplication(): Application = 16 | val app = new Application() 17 | 18 | val totalDudes = 20 19 | 20 | final class Dude: 21 | 22 | // create a new Sprite that uses the image name that we just generated as its source 23 | val dude: Sprite = new Sprite(Texture.from(EggHeadImage)) 24 | 25 | // set the anchor point so the texture is centerd on the sprite 26 | dude.anchor.set(0.5) 27 | 28 | // set a random scale for the dude - no point them all being the same size! 29 | dude.scale.set(0.8 + Math.random() * 0.3) 30 | 31 | // finally lets set the dude to be at a random position.. 32 | dude.x = Math.random() * app.screen.width 33 | dude.y = Math.random() * app.screen.height 34 | 35 | dude.tint = Math.random() * 0xffffff 36 | 37 | // create some extra properties that will control movement : 38 | // create a random direction in radians. This is a number between 0 and PI*2 which is the equivalent of 0 - 360 degrees 39 | var direction: Double = Math.random() * Math.PI * 2 40 | 41 | // this number will be used to modify the direction of the dude over time 42 | var turningSpeed: Double = Math.random() - 0.8 43 | 44 | // create a random speed for the dude between 0 - 2 45 | var speed: Double = 2 + Math.random() * 2 46 | 47 | app.stage.addChild(dude) 48 | end Dude 49 | 50 | val aliens = (0 until totalDudes).map(_ => new Dude) 51 | 52 | // create a bounding box for the little dudes 53 | val dudeBoundsPadding = 100 54 | val dudeBounds = new Rectangle( 55 | -dudeBoundsPadding, 56 | -dudeBoundsPadding, 57 | app.screen.width + dudeBoundsPadding * 2, 58 | app.screen.height + dudeBoundsPadding * 2 59 | ) 60 | 61 | app.ticker.add { (_: Double) => 62 | // iterate through the dudes and update their position 63 | for dude <- aliens do 64 | 65 | dude.direction += dude.turningSpeed * 0.01 66 | dude.dude.x += Math.sin(dude.direction) * dude.speed 67 | dude.dude.y += Math.cos(dude.direction) * dude.speed 68 | dude.dude.rotation = -dude.direction - Math.PI / 2 69 | 70 | // wrap the dudes by testing their bounds... 71 | if dude.dude.x < dudeBounds.x then dude.dude.x += dudeBounds.width 72 | else if dude.dude.x > dudeBounds.x + dudeBounds.width then dude.dude.x -= dudeBounds.width 73 | 74 | if dude.dude.y < dudeBounds.y then dude.dude.y += dudeBounds.height 75 | else if dude.dude.y > dudeBounds.y + dudeBounds.height then dude.dude.y -= dudeBounds.height 76 | 77 | } 78 | 79 | app 80 | end newApplication 81 | end Tinting 82 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/filtersbasic/DisplacementMapFlag.scala: -------------------------------------------------------------------------------- 1 | package demo.filtersbasic 2 | 3 | import demo.assets.pixiFilters.{DisplacementMapRepeat, FlagImage} 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.mod.{filters, Application, Container, Sprite, WRAP_MODES} 6 | import demo.monkeypatching.PIXIPatching.* 7 | 8 | import scala.scalajs.js 9 | 10 | case object DisplacementMapFlag extends PIXIExample: 11 | val name: String = "Displacement Map - Flag" 12 | val pixiUrl: String = 13 | "https://pixijs.io/examples/#/filters-basic/displacement-map-flag.js" 14 | 15 | protected def newApplication(): Application = 16 | 17 | val app = new Application() 18 | 19 | app.stage.interactive = true 20 | 21 | val container = new Container 22 | app.stage.addChild(container) 23 | 24 | val flag = Sprite.from(FlagImage) 25 | container.addChild(flag) 26 | flag.x = 100 27 | flag.y = 100 28 | 29 | val displacementSprite = Sprite.from(DisplacementMapRepeat) 30 | // Make sure the sprite is wrapping. 31 | displacementSprite.texture.baseTexture.wrapMode = WRAP_MODES.REPEAT 32 | val displacementFilter = 33 | new filters.DisplacementFilter(displacementSprite) 34 | displacementFilter.padding = 10 35 | 36 | displacementSprite.position = flag.position 37 | 38 | app.stage.addChild(displacementSprite) 39 | 40 | flag.filters = js.Array(displacementFilter) 41 | 42 | displacementFilter.scale.x = 30 43 | displacementFilter.scale.y = 60 44 | 45 | app.ticker.add { () => 46 | // Offset the sprite position to make vFilterCoord update to larger value. Repeat wrapping makes sure there's still pixels on the coordinates. 47 | displacementSprite.x += 1 48 | // Reset x to 0 when it's over width to keep values from going to very huge numbers. 49 | if displacementSprite.x > displacementSprite.width then displacementSprite.x = 0 50 | 51 | } 52 | 53 | app 54 | end newApplication 55 | end DisplacementMapFlag 56 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/graphics/Simple.scala: -------------------------------------------------------------------------------- 1 | package demo.graphics 2 | 3 | import demo.pixi.PIXIExample 4 | import typings.pixiJs.PIXI.Point 5 | import typings.pixiJs.anon.Antialias as ApplicationOptions 6 | import typings.pixiJs.mod.{Application, Graphics} 7 | 8 | import scala.scalajs.js 9 | import scala.scalajs.js.| 10 | 11 | case object Simple extends PIXIExample: 12 | 13 | val name: String = "Simple" 14 | 15 | val pixiUrl: String = "https://pixijs.io/examples/#/graphics/simple.js" 16 | 17 | override def newApplication(): Application = 18 | val app = new Application(ApplicationOptions().setAntialias(true)) 19 | 20 | val graphics = new Graphics() 21 | 22 | // Rectangle 23 | graphics.beginFill(0xde3249) 24 | graphics.drawRect(50, 50, 100, 100) 25 | graphics.endFill() 26 | 27 | // Rectangle + line style 1 28 | graphics.lineStyle(2, 0xfeeb77, 1) 29 | graphics.beginFill(0x650a5a) 30 | graphics.drawRect(200, 50, 100, 100) 31 | graphics.endFill() 32 | 33 | // Rectangle + line style 2 34 | graphics.lineStyle(10, 0xffbd01, 1) 35 | graphics.beginFill(0xc34288) 36 | graphics.drawRect(350, 50, 100, 100) 37 | graphics.endFill() 38 | 39 | // Rectangle 2 40 | graphics.lineStyle(2, 0xffffff, 1) 41 | graphics.beginFill(0xaa4f08) 42 | graphics.drawRect(530, 50, 140, 100) 43 | graphics.endFill() 44 | 45 | // Circle 46 | graphics.lineStyle(0) // draw a circle, set the lineStyle to zero so the circle doesn't have an outline 47 | graphics.beginFill(0xde3249, 1) 48 | graphics.drawCircle(100, 250, 50) 49 | graphics.endFill() 50 | 51 | // Circle + line style 1 52 | graphics.lineStyle(2, 0xfeeb77, 1) 53 | graphics.beginFill(0x650a5a, 1) 54 | graphics.drawCircle(250, 250, 50) 55 | graphics.endFill() 56 | 57 | // Circle + line style 2 58 | graphics.lineStyle(10, 0xffbd01, 1) 59 | graphics.beginFill(0xc34288, 1) 60 | graphics.drawCircle(400, 250, 50) 61 | graphics.endFill() 62 | 63 | // Ellipse + line style 2 64 | graphics.lineStyle(2, 0xffffff, 1) 65 | graphics.beginFill(0xaa4f08, 1) 66 | graphics.drawEllipse(600, 250, 80, 50) 67 | graphics.endFill() 68 | 69 | // draw a shape 70 | graphics.beginFill(0xff3300) 71 | graphics.lineStyle(4, 0xffd900, 1) 72 | graphics.moveTo(50, 350) 73 | graphics.lineTo(250, 350) 74 | graphics.lineTo(100, 400) 75 | graphics.lineTo(50, 350) 76 | graphics.closePath() 77 | graphics.endFill() 78 | 79 | // draw a rounded rectangle 80 | graphics.lineStyle(2, 0xff00ff, 1) 81 | graphics.beginFill(0x650a5a, 0.25) 82 | graphics.drawRoundedRect(50, 440, 100, 100, 16) 83 | graphics.endFill() 84 | 85 | // draw star 86 | graphics.lineStyle(2, 0xffffff) 87 | graphics.beginFill(0x35cc5a, 1) 88 | graphics.drawStar(360, 370, 5, 50, 5) 89 | graphics.endFill() 90 | 91 | // draw star 2 92 | graphics.lineStyle(2, 0xffffff) 93 | graphics.beginFill(0xffcc5a, 1) 94 | graphics.drawStar(280, 510, 7, 50, 5) 95 | graphics.endFill() 96 | 97 | // draw star 3 98 | graphics.lineStyle(4, 0xffffff) 99 | graphics.beginFill(0x55335a, 1) 100 | graphics.drawStar(470, 450, 4, 50, 5) 101 | graphics.endFill() 102 | 103 | // draw polygon 104 | val path: js.Array[Double | Point] = 105 | js.Array(600, 370, 700, 460, 780, 420, 730, 570, 590, 520) 106 | 107 | graphics.lineStyle(0) 108 | graphics.beginFill(0x3500fa, 1) 109 | graphics.drawPolygon(path) 110 | graphics.endFill() 111 | 112 | app.stage.addChild(graphics) 113 | 114 | app 115 | end newApplication 116 | end Simple 117 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/interaction/Click.scala: -------------------------------------------------------------------------------- 1 | package demo.interaction 2 | 3 | import demo.assets.BunnyImage 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.anon.Antialias as ApplicationOptions 6 | import typings.pixiJs.mod.* 7 | 8 | import scala.scalajs.js 9 | 10 | case object Click extends PIXIExample: 11 | 12 | val name: String = "Click" 13 | 14 | val pixiUrl: String = "https://pixijs.io/examples/#/interaction/click.js" 15 | 16 | def newApplication(): Application = 17 | 18 | val app = new Application(ApplicationOptions().setBackgroundColor(0x1099bb)) 19 | 20 | // Scale mode for all textures, will retain pixelation 21 | settings.SCALE_MODE = SCALE_MODES.NEAREST 22 | 23 | val texture = Texture.from(BunnyImage) 24 | val sprite = new Sprite(texture) 25 | 26 | // Set the initial position 27 | sprite.anchor.set(0.5) 28 | sprite.x = app.screen.width / 2 29 | sprite.y = app.screen.height / 2 30 | 31 | // Opt-in to interactivity 32 | sprite.interactive = true 33 | 34 | // Shows hand cursor 35 | sprite.buttonMode = true 36 | 37 | // Pointers normalize touch and mouse 38 | sprite.on("pointerdown", (_: js.Any) => onClick()) 39 | 40 | // Alternatively, use the mouse & touch events: 41 | // sprite.on('click', onClick); // mouse-only 42 | // sprite.on('tap', onClick); // touch-only 43 | 44 | app.stage.addChild(sprite) 45 | 46 | def onClick(): Unit = 47 | sprite.scale.x *= 1.25 48 | sprite.scale.y *= 1.25 49 | 50 | app 51 | end newApplication 52 | end Click 53 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/meshandshaders/Uniform.scala: -------------------------------------------------------------------------------- 1 | package demo.meshandshaders 2 | 3 | import demo.assets.BackgroundSceneRotate 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.mod.{Application, Geometry, Mesh, Shader, Texture} 6 | import typings.pixiJs.PIXI.Buffer 7 | import demo.monkeypatching.PIXIPatching.* 8 | 9 | import scala.scalajs.js 10 | import scala.scalajs.js.| 11 | 12 | case object Uniform extends PIXIExample: 13 | val name: String = "Uniform" 14 | val pixiUrl: String = "https://pixijs.io/examples/#/mesh-and-shaders/uniforms.js" 15 | 16 | protected def newApplication(): Application = 17 | 18 | val app = new Application() 19 | 20 | val geometry = new Geometry() 21 | .addAttribute( 22 | "aVertexPosition", // the attribute name 23 | js.Array(-100.0, -100, // x, y 24 | 100, -100, // x, y 25 | 100, 100, -100, 100) 26 | .asInstanceOf[js.UndefOr[js.Array[Double] | Buffer]], // x, y 27 | 2, 28 | js.undefined, 29 | js.undefined, 30 | js.undefined, 31 | js.undefined 32 | ) // the size of the attribute 33 | .addAttribute( 34 | "aUvs", // the attribute name 35 | js.Array(0.0, 0, // u, v 36 | 1, 0, // u, v 37 | 1, 1, 0, 1) 38 | .asInstanceOf[js.UndefOr[js.Array[Double] | Buffer]], // u, v 39 | 2, 40 | js.undefined, 41 | js.undefined, 42 | js.undefined, 43 | js.undefined 44 | ) // the size of the attribute 45 | .addIndex(js.Array(0.0, 1, 2, 0, 2, 3)) 46 | 47 | val vertexSrc = 48 | """ 49 | |precision mediump float; 50 | | 51 | | attribute vec2 aVertexPosition; 52 | | attribute vec2 aUvs; 53 | | 54 | | uniform mat3 translationMatrix; 55 | | uniform mat3 projectionMatrix; 56 | | 57 | | varying vec2 vUvs; 58 | | 59 | | void main() { 60 | | 61 | | vUvs = aUvs; 62 | | gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); 63 | | 64 | | } 65 | |""".stripMargin 66 | 67 | val fragmentSrc = """ 68 | |precision mediump float; 69 | | 70 | | varying vec2 vUvs; 71 | | 72 | | uniform sampler2D uSampler2; 73 | | uniform float time; 74 | | 75 | | void main() { 76 | | 77 | | gl_FragColor = texture2D(uSampler2, vUvs + sin( (time + (vUvs.x) * 14.) ) * 0.1 ); 78 | | } 79 | """.stripMargin 80 | 81 | class Uniform(val uSampler2: Texture, var time: Double) extends js.Object 82 | 83 | val uniforms = new Uniform( 84 | Texture.from(BackgroundSceneRotate).asInstanceOf[typings.pixiJs.mod.Texture], 85 | time = 0 86 | ) 87 | 88 | val shader = Shader.from(vertexSrc, fragmentSrc, uniforms) 89 | 90 | val quad = new Mesh(geometry, shader) 91 | 92 | quad.position.set(400, 300) 93 | quad.scale.set(2) 94 | 95 | app.stage.addChild(quad) 96 | 97 | app.ticker.add { () => 98 | quad.rotation += 0.01 99 | quad.shader.asInstanceOf[Shader].uniforms.asInstanceOf[Uniform].time += 0.1 100 | } 101 | 102 | app 103 | end newApplication 104 | end Uniform 105 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/monkeypatching/PIXIPatching.scala: -------------------------------------------------------------------------------- 1 | package demo.monkeypatching 2 | 3 | import typings.pixiJs.PIXI.Ticker 4 | 5 | import scala.language.implicitConversions 6 | import scala.scalajs.js 7 | 8 | object PIXIPatching: 9 | 10 | implicit class TickerWithDoubleAdd(ticker: Ticker): 11 | def add(fn: Double => Unit): Ticker = ticker.add(fn.asInstanceOf[Any => Any]) 12 | 13 | def add(fn: () => Unit): Ticker = ticker.add(_ => fn()) 14 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/pixi/ExampleSelector.scala: -------------------------------------------------------------------------------- 1 | package demo.pixi 2 | 3 | import org.scalajs.dom 4 | import org.scalajs.dom.html 5 | import org.scalajs.dom.raw.XMLHttpRequest 6 | import typings.highlightJs.mod.{highlightBlock, Node} 7 | 8 | import scala.scalajs.js.timers.setTimeout 9 | import scala.scalajs.js 10 | import scala.scalajs.js.annotation.JSImport 11 | 12 | /** This object is dedicated to display the correct example chosen by the user. When the user changes an example, we 13 | * stop the current animation, we load the next one and we fetch the Scala source code from Github, displaying it with 14 | * syntax highlighting using highlight.js 15 | */ 16 | object ExampleSelector: 17 | 18 | private val overallContainer: html.Div = dom.document 19 | .getElementById("overall-container") 20 | .asInstanceOf[html.Div] 21 | 22 | private val welcomeContainer: html.Div = dom.document 23 | .getElementById("welcome-message") 24 | .asInstanceOf[html.Div] 25 | 26 | private val titleH1: html.Heading = 27 | dom.document.getElementById("title").asInstanceOf[html.Heading] 28 | 29 | private val codeDiv: html.Div = 30 | dom.document.getElementById("code-container").asInstanceOf[html.Div] 31 | 32 | private val canvasContainer: html.Div = dom.document 33 | .getElementById("canvas-container") 34 | .asInstanceOf[html.Div] 35 | 36 | private val pixiUrlLink: html.Anchor = dom.document 37 | .getElementById("pixi-url") 38 | .asInstanceOf[html.Anchor] 39 | 40 | def loadAndDisplayCode(example: PIXIExample, pkg: String): Unit = 41 | val request = new XMLHttpRequest 42 | 43 | def display(): Unit = 44 | // checking if the page is still the same when we receive the response 45 | if titleH1.textContent == example.name & request.readyState == 4 & request.status == 200 then 46 | val code = request.responseText 47 | 48 | codeDiv.innerHTML = s"""
    $code
    """ 49 | setTimeout(1) { 50 | 51 | highlightBlock(codeDiv.firstChild.firstChild.asInstanceOf[Node]) 52 | } 53 | 54 | request.open("GET", example.githubUrl(pkg)) 55 | request.send() 56 | 57 | request.onreadystatechange = (_: dom.Event) => display() 58 | end loadAndDisplayCode 59 | 60 | /** Removes the content of the canvas-container div, and puts a new html.Canvas into it. 61 | */ 62 | def changeCanvas(example: PIXIExample): html.Canvas = 63 | val children = canvasContainer.children 64 | for idx <- 0 until children.length do canvasContainer.removeChild(children(idx)) 65 | 66 | titleH1.textContent = example.name 67 | pixiUrlLink.href = example.pixiUrl 68 | canvasContainer.appendChild(example.canvas) 69 | example.canvas 70 | end changeCanvas 71 | 72 | private val menu: html.Div = 73 | dom.document.getElementById("menu").asInstanceOf[html.Div] 74 | 75 | def makeSection(title: String, examples: List[PIXIExample]): Unit = 76 | menu.appendChild { 77 | val div = dom.document.createElement("div").asInstanceOf[html.Div] 78 | div.className = "menu-section" 79 | 80 | val exampleContainer = 81 | dom.document.createElement("div").asInstanceOf[html.Div] 82 | exampleContainer.style.display = "none" 83 | for example <- examples do 84 | val option = dom.document.createElement("div").asInstanceOf[html.Option] 85 | option.className = "example-option" 86 | option.textContent = example.name 87 | option.value = example.name 88 | 89 | option.onclick = (_: dom.MouseEvent) => 90 | println(s"Running example ${example.name}") 91 | welcomeContainer.innerHTML = "" 92 | example.run(title.toLowerCase.filterNot(_ == '-').filterNot(_ == ' ')) 93 | overallContainer.style.display = "block" 94 | 95 | exampleContainer.appendChild(option) 96 | end for 97 | 98 | val titleDiv = dom.document.createElement("div").asInstanceOf[html.Div] 99 | titleDiv.className = "section-header" 100 | titleDiv.textContent = title 101 | titleDiv.style.cursor = "pointer" 102 | titleDiv.onclick = (_: dom.MouseEvent) => 103 | if exampleContainer.style.display == "none" then exampleContainer.style.display = "block" 104 | else exampleContainer.style.display = "none" 105 | 106 | div.appendChild(titleDiv) 107 | div.appendChild(exampleContainer) 108 | 109 | div 110 | } 111 | 112 | @js.native @JSImport("./styles.css", JSImport.Namespace) 113 | object Style extends js.Object 114 | 115 | @js.native @JSImport("./a11y-light.css", JSImport.Namespace) 116 | object `a11y-light.css` extends js.Object 117 | 118 | @main 119 | def main: Unit = 120 | 121 | /** Touch to load */ 122 | Style 123 | `a11y-light.css` 124 | 125 | PIXIExample.allExamples.foreach { case (title, examples) => 126 | makeSection(title, examples) 127 | } 128 | end main 129 | end ExampleSelector 130 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/pixi/PIXIExample.scala: -------------------------------------------------------------------------------- 1 | package demo.pixi 2 | 3 | import demo.demosadvanced.* 4 | import demo.demosbasic.* 5 | import demo.filtersbasic.DisplacementMapFlag 6 | import demo.graphics.Simple 7 | import demo.interaction.Click 8 | import demo.meshandshaders.Uniform 9 | import demo.pluginfilters.Outline 10 | import demo.sprite.{TilingSpriteExample, VideoExample} 11 | import org.scalajs.dom.html 12 | import typings.pixiJs.anon.BaseTexture as StageOptions 13 | import typings.pixiJs.mod.Application 14 | 15 | trait PIXIExample: 16 | 17 | val name: String 18 | 19 | val pixiUrl: String 20 | 21 | def githubUrl(pkg: String): String = 22 | s"""https://raw.githubusercontent.com/ScalablyTyped/Demos/master/pixi/src/main/scala/demo/$pkg/$toString.scala""" 23 | 24 | private var pixiApp: Option[Application] = None 25 | 26 | private def stop(): Unit = 27 | pixiApp match 28 | case Some(app) => 29 | app.destroy( 30 | removeView = true, 31 | stageOptions = StageOptions().setBaseTexture(true) 32 | ) 33 | case None => 34 | end match 35 | pixiApp = None 36 | end stop 37 | 38 | protected def newApplication(): Application 39 | 40 | def run(pkg: String): Unit = 41 | PIXIExample.stopAll() 42 | pixiApp = Some(newApplication()) 43 | ExampleSelector.changeCanvas(this) 44 | ExampleSelector.loadAndDisplayCode(this, pkg) 45 | end run 46 | 47 | def canvas: html.Canvas = pixiApp.get.view 48 | end PIXIExample 49 | 50 | object PIXIExample: 51 | 52 | val allExamples: Map[String, List[PIXIExample]] = Map( 53 | "DEMOS-BASIC" -> List(Basics, Containers, ContainerPivot, Tinting), 54 | "DEMOS-ADVANCED" -> List(MouseTrail, StarWarp), 55 | "SPRITE" -> List(TilingSpriteExample, VideoExample), 56 | "GRAPHICS" -> List(Simple), 57 | "INTERACTION" -> List(Click), 58 | "MESH-AND-SHADERS" -> List(Uniform), 59 | "FILTERS-BASIC" -> List(DisplacementMapFlag), 60 | "PLUGIN-FILTERS" -> List(Outline) 61 | ) 62 | 63 | def stopAll(): Unit = 64 | allExamples.values.flatten.foreach(_.stop()) 65 | end PIXIExample 66 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/pluginfilters/Outline.scala: -------------------------------------------------------------------------------- 1 | package demo.pluginfilters 2 | 3 | import demo.assets.BunnyImage 4 | import demo.pixi.PIXIExample 5 | import typings.pixiFilterGlow.PIXI.filters.GlowFilterOptions 6 | import typings.pixiFilterGlow.mod.{GlowFilter} 7 | import typings.pixiFilterOutline.mod.OutlineFilter 8 | import typings.pixiJs.mod.{Application, Sprite, Texture} 9 | 10 | import scala.scalajs.js 11 | 12 | case object Outline extends PIXIExample: 13 | val name: String = "Outline" 14 | val pixiUrl: String = "https://pixijs.io/examples/#/plugin-filters/outline.js" 15 | 16 | protected def newApplication(): Application = 17 | val app = new Application() 18 | 19 | app.stage.position.set(400, 300) 20 | 21 | val outlineFilterBlue = new OutlineFilter(2, 0x99ff99) 22 | val outlineFilterRed = new GlowFilter( 23 | GlowFilterOptions() 24 | .setOuterStrength(15) 25 | .setDistance(2) 26 | .setInnerStrength(1) 27 | .setColor(0xff9999) 28 | .setQuality(0.5) 29 | ) 30 | 31 | def filterOn(sprite: Sprite): Unit = 32 | sprite.filters = js.Array(outlineFilterRed) 33 | 34 | def filterOff(sprite: Sprite): Unit = 35 | sprite.filters = js.Array(outlineFilterBlue) 36 | 37 | val texture = Texture.from(BunnyImage) 38 | 39 | for _ <- 0 until 20 do 40 | val bunny = new Sprite(texture) 41 | bunny.interactive = true 42 | bunny.position.set((Math.random() * 2 - 1) * 300, (Math.random() * 2 - 1) * 200) 43 | bunny.scale.x = (Math.random() * 3) + 1 44 | bunny 45 | .on("pointerover", () => filterOn(bunny)) 46 | .on("pointerout", () => filterOff(bunny)) 47 | filterOff(bunny) 48 | app.stage.addChild(bunny) 49 | end for 50 | 51 | app 52 | end newApplication 53 | end Outline 54 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/sprite/TilingSpriteExample.scala: -------------------------------------------------------------------------------- 1 | package demo.sprite 2 | 3 | import demo.assets.P2Image 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.mod.{Application, Texture, TilingSprite} 6 | import demo.monkeypatching.PIXIPatching.* 7 | 8 | case object TilingSpriteExample extends PIXIExample: 9 | 10 | val name: String = "Tiling Sprite" 11 | 12 | val pixiUrl: String = "https://pixijs.io/examples/#/sprite/tiling-sprite.js" 13 | 14 | def newApplication(): Application = 15 | 16 | val app = new Application() 17 | 18 | // create a texture from an image path 19 | val texture = Texture.from(P2Image) 20 | 21 | /* create a tiling sprite ... 22 | * requires a texture, a width and a height 23 | * in WebGL the image size should preferably be a power of two 24 | */ 25 | val tilingSprite = 26 | new TilingSprite(texture, app.screen.width, app.screen.height) 27 | app.stage.addChild(tilingSprite) 28 | 29 | var count: Double = 0 30 | 31 | app.ticker.add { () => 32 | 33 | count += 0.005 34 | 35 | tilingSprite.tileScale.x = 2 + Math.sin(count) 36 | tilingSprite.tileScale.y = 2 + Math.cos(count) 37 | 38 | tilingSprite.tilePosition.x += 1 39 | tilingSprite.tilePosition.y += 1 40 | 41 | } 42 | 43 | app 44 | end newApplication 45 | end TilingSpriteExample 46 | -------------------------------------------------------------------------------- /pixi/src/main/scala/demo/sprite/VideoExample.scala: -------------------------------------------------------------------------------- 1 | package demo.sprite 2 | 3 | import demo.assets.TheVideo 4 | import demo.pixi.PIXIExample 5 | import typings.pixiJs.anon.Antialias as ApplicationOptions 6 | import typings.pixiJs.mod.{Application, Graphics, Sprite, Texture} 7 | 8 | import scala.scalajs.js 9 | 10 | case object VideoExample extends PIXIExample: 11 | 12 | val name: String = "Video" 13 | 14 | val pixiUrl: String = "https://pixijs.io/examples/#/sprite/video.js" 15 | 16 | override def newApplication(): Application = 17 | val app = new Application(ApplicationOptions().setTransparent(true)) 18 | 19 | // Create play button that can be used to trigger the video 20 | val button = new Graphics() 21 | .beginFill(0x0, 0.5) 22 | .drawRoundedRect(0, 0, 100, 100, 10) 23 | .endFill() 24 | .beginFill(0xffffff) 25 | .moveTo(36, 30) 26 | .lineTo(36, 70) 27 | .lineTo(70, 50) 28 | 29 | // Position the button 30 | button.x = (app.screen.width - button.width) / 2 31 | button.y = (app.screen.height - button.height) / 2 32 | 33 | // Enable interactivity on the button 34 | button.interactive = true 35 | button.buttonMode = true 36 | 37 | // Add to the stage 38 | app.stage.addChild(button) 39 | 40 | // Listen for a click/tap event to start playing the video 41 | // this is useful for some mobile platforms. For example: 42 | // ios9 and under cannot render videos in PIXI without a 43 | // polyfill - https://github.com/bfred-it/iphone-inline-video 44 | // ios10 and above require a click/tap event to render videos 45 | // that contain audio in PIXI. Videos with no audio track do 46 | // not have this requirement 47 | button.on("pointertap", (_: js.Any) => onPlayVideo()) 48 | 49 | def onPlayVideo() = 50 | 51 | // Don't need the button anymore 52 | button.destroy() 53 | 54 | // create a video texture from a path 55 | val texture = Texture.from(TheVideo) 56 | 57 | // create a new Sprite using the video texture (yes it's that easy) 58 | val videoSprite = new Sprite(texture) 59 | 60 | // Stetch the fullscreen 61 | videoSprite.width = app.screen.width 62 | videoSprite.height = app.screen.height 63 | 64 | app.stage.addChild(videoSprite) 65 | end onPlayVideo 66 | 67 | app 68 | end newApplication 69 | end VideoExample 70 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.9.0 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.1") 2 | addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1") 3 | addSbtPlugin("org.scalablytyped.converter" % "sbt-converter" % "1.0.0-beta44") -------------------------------------------------------------------------------- /project/scalafmt.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.0.0") 2 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Demos for ScalablyTyped 2 | 3 | This is a collection of tiny demo projects to show off how we can use javascript libraries from scala.js 4 | 5 | ## React demos 6 | 7 | All react demos are now moved to separate repositories by the react library they use. 8 | See slinky demos [here](https://github.com/ScalablyTyped/SlinkyTypedDemos) 9 | 10 | ## Browser demos 11 | 12 | ### d3 13 | [Demo](https://scalablytyped.github.io/Demos/d3/) 14 | 15 | It uses d3 to generate a rather fancy spinning globe. Demo is converted from [here](https://bl.ocks.org/animateddata/1f6522d3fcec29c01e7f4a5894e1fd94) 16 | 17 | `sbt> d3/start` starts a webpack-dev-server at http://localhost:8001 . 18 | 19 | ### google-maps 20 | [Demo](https://scalablytyped.github.io/Demos/google-maps/) 21 | 22 | The demo loads the google maps javascript as distributed by google (see [index.html](./google-maps/assets/index.html) ). 23 | It's very simple usage, it just shows the location of a few beaches. 24 | 25 | `sbt> google-maps/start` starts a webpack-dev-server at http://localhost:8002 . 26 | 27 | ### jquery 28 | [Demo](https://scalablytyped.github.io/Demos/jquery/) 29 | 30 | This demo shows how to interact with old-style javascript. 31 | Jqueryui is a global library (as in, not a module), so you'll see the code touches an object to include it. 32 | It also extends jquery with more functionality, so you'll see an explicit cast to tell the compiler about this. 33 | This is poor mans interface augmentation (a mechanism by which typescript does this automatically) 34 | 35 | #### Libraries used 36 | 37 | - jquery 38 | - jqueryui 39 | 40 | `sbt> jquery/start` starts a webpack-dev-server at http://localhost:8003 . 41 | 42 | ### Vue 43 | [Demo](https://scalablytyped.github.io/Demos/vue/) 44 | 45 | This demo showcases a pretty simple todo app (stolen and adapted from [scalajs-vue](https://github.com/fancellu/scalajs-vue/)). 46 | Some templating is done in [index.html](./vue/assets/index.html), while a bunch of stuff is done in Scala. 47 | 48 | From its design it's pretty clear that Vue was designed by javascript people. 49 | Trying to obtain type safety in this mess will probably never be worth it, 50 | but at least now you can try! :) 51 | `sbt> vue/start` starts a webpack-dev-server at http://localhost:8004 . 52 | 53 | 54 | ### Three.js 55 | [Demo](https://scalablytyped.github.io/Demos/three/) 56 | 57 | A fancy animation of a horse, stolen from [three.js demos](https://github.com/mrdoob/three.js/blob/master/examples/webgl_morphtargets_horse.html). 58 | `sbt> three/start` starts a webpack-dev-server at http://localhost:8005 . 59 | 60 | ### Reveal.js 61 | [Demo](https://scalablytyped.github.io/Demos/reveal/) 62 | 63 | Write your talks in scala.js! This uses highlight.js and reveal.js along with 64 | scalajs-react. adapted from [scala-reveal-js](https://github.com/pheymann/scala-reveal-js), 65 | `sbt> reveal/start` starts a webpack-dev-server at http://localhost:8006 . 66 | 67 | ### Chart.js 68 | [Demo](https://scalablytyped.github.io/Demos/chart/) 69 | 70 | Simple charting using canvas elements. Shows off how to work with the DOM as well 71 | as how to use chart.js. Heavily adapted from the [retyped demo](https://github.com/Retyped/Demos/tree/master/ChartJsDemo), 72 | `sbt> chart/start` starts a webpack-dev-server at http://localhost:8007 . 73 | 74 | ### Angular 8 75 | [Demo](https://scalablytyped.github.io/Demos/angular/) 76 | 77 | Let's be nice and say that Angular is a reasonable alternative for creating a frontend app. 78 | If you agree, now is your chance to use it with Scala.js. 79 | 80 | Adapted from [sherpal's prototype](https://github.com/sherpal/AngularScalaPOC). 81 | `sbt> angular/start` starts a webpack-dev-server at http://localhost:8008 . 82 | 83 | ### P5 84 | [Demo](https://scalablytyped.github.io/Demos/p5/index.html) 85 | 86 | Demo adapted from [documentation](https://p5js.org/examples/instance-mode-instantiation.html) 87 | `sbt> p5/start` starts a webpack-dev-server at http://localhost:8009 . 88 | 89 | ### Leaflet 90 | [Demo](https://scalablytyped.github.io/Demos/leaflet/index.html) 91 | 92 | Demo adapted from [scalajs-leaflet](https://github.com/fancellu/scalajs-leaflet/blob/master/example/src/main/scala/example/QuickStartLeaflet.scala) 93 | `sbt> leaflet/start` starts a webpack-dev-server at http://localhost:8010 . 94 | 95 | 96 | ### Onsenui 97 | [Demo](https://scalablytyped.github.io/Demos/onsenui/index.html) 98 | Adapted from [documentation](https://onsen.io/v2/guide/jquery/) 99 | 100 | `sbt> onsenui/start` starts a webpack-dev-server at http://localhost:8011 . 101 | 102 | ### phaser 103 | [Demo](https://scalablytyped.github.io/Demos/phaser/index.html) 104 | Adapted from [animation/create-from-sprite-config example](http://phaser.io/examples/v3/view/animation/create-from-sprite-config) 105 | 106 | `sbt> phaser/start` starts a webpack-dev-server at http://localhost:8012 . 107 | 108 | ### Pixi 109 | [Demo](https://scalablytyped.github.io/Demos/pixi/index.html) 110 | This is the translation of some of the [examples](https://pixijs.io/examples) into Scala. 111 | 112 | [Pixi.js](https://pixijs.io) is a library to render blazingly fast 2D animations on Canvas, using WebGL under the hood. 113 | 114 | `sbt> pixi/start` starts a webpack-dev-server at http://localhost:8013 . 115 | 116 | You will be presented with a menu that has the same structure as the examples from the Pixi website. 117 | 118 | ## Electron 119 | Implements the backend/mainprocess part of an Electron app in Scala.js, 120 | though it would be easy to do the frontend as well (in another module). 121 | 122 | Start the project like this: 123 | 124 | ``` 125 | sbt>electron/run 126 | ``` 127 | 128 | Again adapted from [sherpal's work](https://github.com/sherpal/Scala.js-Electron-App-Example). 129 | 130 | ## Node demos 131 | 132 | ### lodash 133 | This is a very simple app which uses a few functions from lodash. 134 | `sbt> lodash/run` runs the demo in node. 135 | 136 | ### node-express 137 | This demo is a HTTP endpoint written in express, which runs on node. 138 | Adapted from [this](https://github.com/BrianDGLS/express-ts) 139 | 140 | `sbt> node-express/run` will start it. 141 | 142 | You'll need for instance `curl` to test it: 143 | ```bash 144 | > curl http://localhost:3000/welcome 145 | #Hello, World! 146 | 147 | > curl http://localhost:3000/welcome/foo 148 | # Hello, foo! 149 | ``` 150 | 151 | ### typescript 152 | 153 | `sbt> typescript/run` runs the typescript compiler on two files (one of which is meant to fail). 154 | It accepts parameters to specify other files if you want to play around. 155 | 156 | ### cypress 157 | 158 | `sbt> cypress/run` runs a basic test 159 | 160 | ## Your demo here! :) 161 | Pull requests most welcome! 162 | -------------------------------------------------------------------------------- /reveal/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reveal.js demo 7 | 8 | 9 | 10 |
    11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /reveal/src/main/scala/demo/Demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalajs.dom 4 | import typings.highlightJs.mod.initHighlightingOnLoad 5 | import typings.reveal.{RevealOptions, RevealStatic} 6 | 7 | import scala.scalajs.js 8 | import scala.scalajs.js.annotation.JSImport 9 | 10 | object Demo: 11 | @main 12 | def main: Unit = 13 | // Touch to load 14 | Includes.HighlightingCss 15 | Includes.WhiteThemeCss 16 | Includes.RevealCss 17 | Includes.Reveal 18 | Includes.ZoomJs 19 | 20 | /* initialize highlight.js */ 21 | initHighlightingOnLoad() 22 | 23 | /* render talk before we initialize Reveal */ 24 | MyTalk.Talk.applyGeneric(())().renderIntoDOM(dom.document.body) 25 | 26 | Includes.Reveal.initialize( 27 | RevealOptions() 28 | .setWidth("80%") 29 | .setHeight("100%") 30 | .setControls(false) 31 | .setProgress(false) 32 | .setHistory(false) 33 | .setCenter(true) 34 | .setTransition("none") 35 | ) 36 | end main 37 | end Demo 38 | 39 | object Includes: 40 | 41 | /* customize import and use as module, even though the typings originally were global */ 42 | @JSImport("reveal.js/js/reveal.js", JSImport.Namespace) 43 | @js.native 44 | object Reveal extends RevealStatic 45 | 46 | @JSImport("reveal.js/plugin/zoom-js/zoom.js", JSImport.Namespace) 47 | @js.native 48 | object ZoomJs extends RevealStatic 49 | 50 | @JSImport("reveal.js/lib/css/zenburn.css", JSImport.Namespace) 51 | @js.native 52 | object HighlightingCss extends js.Object 53 | 54 | @JSImport("reveal.js/css/theme/white.css", JSImport.Namespace) 55 | @js.native 56 | object WhiteThemeCss extends js.Object 57 | 58 | @JSImport("reveal.js/css/reveal.css", JSImport.Namespace) 59 | @js.native 60 | object RevealCss extends js.Object 61 | end Includes 62 | -------------------------------------------------------------------------------- /reveal/src/main/scala/demo/MyTalk.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import demo.PresentationUtil.Enumeration.* 4 | import demo.PresentationUtil.* 5 | import japgolly.scalajs.react.component.Scala.Component 6 | import japgolly.scalajs.react.{CtorType, ScalaComponent} 7 | import japgolly.scalajs.react.vdom.html_<^.* 8 | 9 | object MyTalk: 10 | 11 | val chapter1 = chapter( 12 | chapterSlide( 13 | <.h2("Build your presentations with ScalaJS + reveal.js"), 14 | <.br, 15 | <.h4("move down (down-arrow)") 16 | ), 17 | headerSlide( 18 | "reveal.js commands", 19 | <.p("Press 'f' to go full-screen and ESC to see an overview of your slides."), 20 | <.br, 21 | <.p("You can navigate to the right and down.") 22 | ), 23 | headerSlide( 24 | "My Header", 25 | <.h3("Headers everywhere") 26 | ), 27 | headerSlide( 28 | "Enumeration", 29 | Enumeration( 30 | Item.stable("always show this item"), 31 | Item.fadeIn("I fade in"), 32 | Item.stable("I am also always here") 33 | ) 34 | ), 35 | headerSlide( 36 | "Code, so much code", 37 | scalaC(""" 38 | def main(args: Array[String]): Unit = { 39 | println("hello, world") 40 | } 41 | """), 42 | scalaFragment(""" 43 | def moreSideEffects(): Unit = { 44 | println("pop in") 45 | } 46 | """) 47 | ), 48 | noHeaderSlide( 49 | <.h3("Or have a blank slide") 50 | ) 51 | ) 52 | 53 | val chapter2 = chapter( 54 | chapterSlide( 55 | <.h2("Where can I find more information?") 56 | ), 57 | headerSlide( 58 | "about reveal.js", 59 | <.a( 60 | ^.href := "https://github.com/hakimel/reveal.js/", 61 | "reveal.js" 62 | ) 63 | ), 64 | headerSlide( 65 | "about ScalaJS", 66 | <.a( 67 | ^.href := "https://www.scala-js.org", 68 | "ScalaJS" 69 | ) 70 | ) 71 | ) 72 | 73 | val Talk: Component[Unit, Unit, Unit, CtorType.Nullary] = ScalaComponent 74 | .builder[Unit]("Presentation") 75 | .renderStatic( 76 | <.div( 77 | ^.cls := "reveal", 78 | <.div( 79 | ^.cls := "slides", 80 | chapter1, 81 | chapter2 82 | ) 83 | ) 84 | ) 85 | .build 86 | end MyTalk 87 | -------------------------------------------------------------------------------- /reveal/src/main/scala/demo/PresentationUtil.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import japgolly.scalajs.react.vdom.TagOf 4 | import japgolly.scalajs.react.vdom.html_<^.* 5 | import org.scalajs.dom 6 | import org.scalajs.dom.raw.HTMLElement 7 | 8 | object PresentationUtil: 9 | 10 | val font = HtmlTag("font") 11 | 12 | val dataBackground = VdomAttr("data-background") 13 | val dataBackgroundColor = VdomAttr("data-background-color") 14 | val dataBackgroundSize = VdomAttr("data-background-size") 15 | val dataTrim = VdomAttr("data-trim") := "" 16 | val dataNoEscape = VdomAttr("data-noescape") := "" 17 | 18 | def chapter(slides: TagMod*): TagOf[HTMLElement] = <.section(slides*) 19 | 20 | def header(text: String, cls: String): TagOf[HTMLElement] = 21 | <.div( 22 | ^.cls := cls, 23 | <.p(text) 24 | ) 25 | 26 | // 100% side-effect full 27 | private def removeHeader(): Unit = 28 | val headerElements = dom.document.getElementsByClassName("slide-header") 29 | 30 | (0 until headerElements.length).foreach { id => 31 | val element = headerElements(id) 32 | 33 | element.parentNode.removeChild(element) 34 | } 35 | end removeHeader 36 | 37 | private def cleanSlide(content: TagOf[HTMLElement]): TagOf[HTMLElement] = 38 | removeHeader() 39 | 40 | content 41 | end cleanSlide 42 | 43 | private val ChapterSlideProps = Seq( 44 | (dataBackgroundColor := "#363633"), 45 | (dataBackgroundSize := "30%") 46 | ) 47 | 48 | def chapterSlide(content: TagMod*): TagOf[HTMLElement] = cleanSlide( 49 | <.section( 50 | (ChapterSlideProps ++: content)* 51 | ) 52 | ) 53 | 54 | def noHeaderSlide(content: TagOf[HTMLElement]*): TagOf[HTMLElement] = cleanSlide( 55 | <.section( 56 | content* 57 | ) 58 | ) 59 | 60 | def headerSlide(headerStr: String, content: TagOf[HTMLElement]*): TagOf[HTMLElement] = cleanSlide( 61 | <.section( 62 | (header(headerStr, "slide-header") +: content)* 63 | ) 64 | ) 65 | 66 | private def rawCode(language: String, codeStr: String): TagOf[HTMLElement] = 67 | <.code( 68 | ^.cls := language, 69 | dataTrim, 70 | dataNoEscape, 71 | codeStr 72 | ) 73 | 74 | def bash(codeStr: String): TagOf[HTMLElement] = <.pre(rawCode("Bash", codeStr)) 75 | def scalaC(codeStr: String): TagOf[HTMLElement] = <.pre(rawCode("Scala", codeStr)) 76 | def haskell(codeStr: String): TagOf[HTMLElement] = <.pre(rawCode("Haskell", codeStr)) 77 | def lisp(codeStr: String): TagOf[HTMLElement] = <.pre(rawCode("Lisp", codeStr)) 78 | 79 | private def rawCodeFragment(language: String, codeStr: String): TagOf[HTMLElement] = 80 | <.pre( 81 | ^.cls := "fragment fade-in", 82 | rawCode(language, codeStr) 83 | ) 84 | 85 | def scalaFragment(codeStr: String): TagOf[HTMLElement] = rawCodeFragment("Scala", codeStr) 86 | def haskellFragment(codeStr: String): TagOf[HTMLElement] = rawCodeFragment("Haskell", codeStr) 87 | def lispFragment(codeStr: String): TagOf[HTMLElement] = rawCodeFragment("Lisp", codeStr) 88 | 89 | object Enumeration: 90 | 91 | object Item: 92 | 93 | def stable(content: TagOf[HTMLElement]): TagOf[HTMLElement] = <.li(content) 94 | def stable(content: String): TagOf[HTMLElement] = <.li(<.p(content)) 95 | def fadeIn(content: TagOf[HTMLElement]): TagOf[HTMLElement] = <.li(^.cls := "fragment fade-in", content) 96 | def fadeIn(content: String): TagOf[HTMLElement] = <.li(^.cls := "fragment fade-in", <.p(content)) 97 | end Item 98 | 99 | def apply(head: TagOf[HTMLElement], tail: TagOf[HTMLElement]*): TagOf[HTMLElement] = 100 | <.ul((head +: tail)*) 101 | end Enumeration 102 | end PresentationUtil 103 | -------------------------------------------------------------------------------- /three/src/main/js/Horse.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScalablyTyped/Demos/558213f6e21e6afbc6f015e06d053038f3a4e66f/three/src/main/js/Horse.glb -------------------------------------------------------------------------------- /three/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Three.js demo 7 | 15 | 16 | 17 |
    18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /three/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import org.scalablytyped.runtime.TopLevel 2 | import typings.std.global.{document, requestAnimationFrame, window} 3 | import typings.std.{FrameRequestCallback, HTMLDivElement, UIEvent, Window, stdStrings} 4 | import typings.three.mod.{Math as ThreeMath, *} 5 | 6 | import scala.scalajs.js 7 | import scala.scalajs.js.Date 8 | import scala.scalajs.js.annotation.JSImport 9 | 10 | val radius = 600 11 | 12 | @main 13 | def main: Unit = 14 | 15 | val container: HTMLDivElement = document.createElement_div(stdStrings.div) 16 | document.body.appendChild(container) 17 | 18 | val info: HTMLDivElement = document.createElement_div(stdStrings.div) 19 | info.style.position = "absolute" 20 | info.style.top = "10px" 21 | info.style.width = "100%" 22 | info.style.textAlign = "center" 23 | info.innerHTML = 24 | """three.js webgl - morph targets - horse. model by mirada from rome""" 25 | container.appendChild(info) 26 | 27 | val camera = new PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000) 28 | camera.position.y = 300 29 | val target = new Vector3(0, 150, 0) 30 | 31 | val scene = new Scene() 32 | scene.background = new Color(0xf0f0f0) 33 | 34 | val light1 = new DirectionalLight(0xefefff, 1.5) 35 | light1.position.set(1, 1, 1).normalize() 36 | scene.add(light1) 37 | 38 | val light2 = new DirectionalLight(0xffefef, 1.5) 39 | light2.position.set(-1, -1, -1).normalize() 40 | scene.add(light2) 41 | 42 | var mixerOpt: js.UndefOr[AnimationMixer] = js.undefined 43 | 44 | typings.std.global.console.warn(HorseModel) 45 | new GLTFLoader().load( 46 | HorseModel, 47 | gltf => 48 | val mesh = gltf.scene.children(0) 49 | mesh.scale.set(1.5, 1.5, 1.5) 50 | scene.add(mesh) 51 | val mixer = new AnimationMixer(mesh) 52 | mixer.clipAction(gltf.animations(0)).setDuration(1).play() 53 | mixerOpt = mixer 54 | ) 55 | 56 | val renderer = new WebGLRenderer() 57 | renderer.setPixelRatio(window.devicePixelRatio) 58 | renderer.setSize(window.innerWidth, window.innerHeight) 59 | container.appendChild(renderer.domElement) 60 | 61 | val onWindowResize: js.ThisFunction1[Window, UIEvent, js.Any] = (window, _) => 62 | camera.aspect = window.innerWidth / window.innerHeight 63 | camera.updateProjectionMatrix() 64 | renderer.setSize(window.innerWidth, window.innerHeight) 65 | 66 | window.addEventListener_resize(stdStrings.resize, onWindowResize, false) 67 | 68 | var prevTime = Date.now() 69 | var theta = 0.0 70 | 71 | def animate: FrameRequestCallback = time => 72 | theta += 0.1 73 | 74 | camera.position.x = radius * Math.sin(ThreeMath.degToRad(theta)) 75 | camera.position.z = radius * Math.cos(ThreeMath.degToRad(theta)) 76 | 77 | camera.lookAt(target) 78 | 79 | mixerOpt.foreach { mixer => 80 | mixer.update((time - prevTime) * 0.001) 81 | prevTime = time; 82 | } 83 | 84 | renderer.render(scene, camera) 85 | requestAnimationFrame(animate) 86 | 87 | animate(0) 88 | end main 89 | 90 | /* Somewhat awkward that a bunch of the needed code live in `examples/`, which we don't currently convert */ 91 | @JSImport("three/examples/jsm/loaders/GLTFLoader", "GLTFLoader") 92 | @js.native 93 | class GLTFLoader() extends Loader: 94 | def load(url: String, onLoad: js.Function1[GLTF, Unit]): Unit = js.native 95 | 96 | trait GLTF extends js.Object: 97 | val animations: js.Array[AnimationClip] 98 | val scene: Scene 99 | val scenes: js.Array[Scene] 100 | val cameras: js.Array[Camera] 101 | val asset: js.Object 102 | end GLTF 103 | 104 | 105 | @JSImport("./Horse.glb", JSImport.Default) 106 | @js.native 107 | object HorseModel extends TopLevel[String] 108 | -------------------------------------------------------------------------------- /typescript/src/main/resources/bad.js: -------------------------------------------------------------------------------- 1 | var bad = function (num) { return console.log("number was " + num); }; 2 | bad_name(42); 3 | -------------------------------------------------------------------------------- /typescript/src/main/resources/bad.ts: -------------------------------------------------------------------------------- 1 | const bad = (num: number) => console.log(`number was ${num}`); 2 | 3 | bad_name(42); -------------------------------------------------------------------------------- /typescript/src/main/resources/good.js: -------------------------------------------------------------------------------- 1 | var good = function (num) { return console.log("number was " + num); }; 2 | good(42); 3 | -------------------------------------------------------------------------------- /typescript/src/main/resources/good.ts: -------------------------------------------------------------------------------- 1 | const good = (num: number) => console.log(`number was ${num}`); 2 | 3 | good(42); -------------------------------------------------------------------------------- /typescript/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import typings.node.global.console 2 | import typings.node.processMod.^ as process 3 | import typings.typescript.mod as ts 4 | 5 | import scala.scalajs.js 6 | import scala.scalajs.js.| 7 | 8 | def compile(fileNames: js.Array[String], options: ts.CompilerOptions): Unit = 9 | val program: ts.Program = ts.createProgram(fileNames, options) 10 | val emitResult = program.emit() 11 | 12 | val allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics) 13 | 14 | allDiagnostics.foreach { diagnostic => 15 | val baseMessage = ts.flattenDiagnosticMessageText(Knowledge.force(diagnostic.messageText), "\n") 16 | 17 | val message = diagnostic.file.fold(baseMessage) { file => 18 | val pos = file.getLineAndCharacterOfPosition(diagnostic.start.get) 19 | s"${diagnostic.file.get.fileName} (${pos.line + 1},${pos.character + 1}): $baseMessage" 20 | } 21 | console.log(message) 22 | } 23 | 24 | val exitCode = if emitResult.emitSkipped then 1 else 0 25 | console.log(s"Process exiting with code '$exitCode'.") 26 | process.exit(exitCode) 27 | end compile 28 | 29 | @main def main: Unit = 30 | val files: js.Array[String] = 31 | process.argv.drop(2) match 32 | case empty if empty.length == 0 => 33 | js.Array( 34 | "typescript/src/main/resources/good.ts", 35 | "typescript/src/main/resources/bad.ts" 36 | ) 37 | case nonEmpty => nonEmpty 38 | 39 | compile( 40 | files, 41 | ts.CompilerOptions().setNoEmitOnError(true).setNoImplicitAny(true).setOutDir("typescript/target/temp") 42 | ) 43 | end main 44 | 45 | object Knowledge: 46 | def force(a: String | ts.DiagnosticMessageChain): ts.DiagnosticMessageChain = 47 | a.asInstanceOf[ts.DiagnosticMessageChain] 48 | -------------------------------------------------------------------------------- /vue/src/main/js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scala.js+Vue.js sample application! 5 | 6 | 38 | 39 | 40 | 41 | 42 |
    43 |
    44 | Title= 45 | 46 | 47 |
  • 48 | 49 | {{todo.content}} 50 | 51 | 52 |
  • 53 |
    54 | 55 | Tasks# {{todos.length}}
    56 | N={{n}}
    57 |
    58 |
    data JSON: {{$data | json 2}}
    59 | 60 |
    61 | Using a v-text directive title= 62 |
    Smooth CSS animation: 63 | 64 | 65 |
    66 |
    67 |
    68 |
    Title reversed={{title | reverse}}
    69 |
    Title wrapper={{title | wrap('<<','>>')}}
    70 |
    xx
    71 | Special content 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |

    80 | Todos computed: {{ todosComputed }} 81 |
    82 |
    83 | 84 | 85 |
    86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /vue/src/main/scala/demo.scala: -------------------------------------------------------------------------------- 1 | import Knowledge.* 2 | import org.scalablytyped.runtime.StringDictionary 3 | import typings.vue.typesOptionsMod.* 4 | import typings.vue.typesVnodeMod.VNodeDirective 5 | import typings.vue.typesVueMod.CombinedVueInstance 6 | 7 | import scala.scalajs.js 8 | import scala.scalajs.js.ThisFunction0 9 | import scala.scalajs.js.annotation.JSImport 10 | 11 | @js.native 12 | trait DemoVue extends Vue: 13 | var title: String = js.native 14 | var n: Double = js.native 15 | var todos: js.Array[DemoVueTodo] = js.native 16 | end DemoVue 17 | 18 | trait DemoVueTodo extends js.Object: 19 | var done: Boolean 20 | val content: String 21 | 22 | trait Methods extends js.Object: 23 | val clickHandler: js.ThisFunction0[DemoVue, Unit] 24 | val addTask: js.ThisFunction0[DemoVue, Unit] 25 | val change1st: js.ThisFunction0[DemoVue, Unit] 26 | val remove: js.ThisFunction1[DemoVue, Int, Unit] 27 | val flipAll: js.ThisFunction0[DemoVue, Unit] 28 | end Methods 29 | 30 | trait Data extends js.Object: 31 | val message: String 32 | val title: String 33 | val todos: js.Array[DemoVueTodo] 34 | val barValue: Int 35 | val n: Int 36 | end Data 37 | 38 | trait Computed extends js.Object: 39 | val todosComputed: js.ThisFunction0[DemoVue, js.Array[String]] 40 | 41 | @main 42 | def main: Unit = 43 | 44 | val tasks = js.Array("Learn JavaScript", "Learn Vue.js", "Learn Scala.js") 45 | 46 | def ts = new java.util.Date().toString 47 | 48 | Vue.component( 49 | "my-component", 50 | ComponentOptions[ 51 | DemoVue, 52 | ThisFunction0[DemoVue, Data], 53 | Methods, 54 | Computed, 55 | PropsDefinition[DemoVue], 56 | DefaultProps 57 | ]() 58 | .setProps(js.Array("myMsg")) 59 | .setTemplate("

    A custom component with msg {{myMsg}} default content

    ") 60 | ) 61 | 62 | Vue.directive( 63 | "mydirective", 64 | DirectiveOptions().setUpdate { (el, directive, _, _) => 65 | val dir = directive.asInstanceOf[VNodeDirective] 66 | el.innerHTML = "This comes from my-directive with contents " + dir.value + " and expression " + dir.expression 67 | } 68 | ) 69 | 70 | val demoOpt = 71 | ComponentOptions[DemoVue, ThisFunction0[DemoVue, Data], Methods, Computed, PropsDefinition[ 72 | DemoVue 73 | ], DefaultProps]() 74 | .setEl("#demo") 75 | .setData(_ => 76 | new Data: 77 | val message = "Hello Vue.js!!!!!" 78 | val title = "Todo App" 79 | val todos = tasks.map(c => 80 | new DemoVueTodo: 81 | var done = c == tasks.head 82 | val content = c 83 | ) 84 | val barValue = 100 85 | val n = 0 86 | ) 87 | .setMethods(new Methods: 88 | val clickHandler = demoVue => demoVue.n -= 1 89 | 90 | val addTask = demoVue => 91 | demoVue.todos.append(new DemoVueTodo: 92 | var done = false 93 | val content = s"new $ts" 94 | ) 95 | 96 | val change1st = demoVue => 97 | Vue.set( 98 | demoVue.todos, 99 | 0, 100 | new DemoVueTodo: 101 | var done = false 102 | val content = ts 103 | ) 104 | 105 | val remove = (demoVue, idx) => Vue.delete(demoVue.todos, idx) 106 | 107 | val flipAll = demoVue => demoVue.todos.foreach(td => td.done = !td.done) 108 | ) 109 | .setComputed( 110 | Knowledge.isAccessors( 111 | new Computed: 112 | val todosComputed = (demoVue: DemoVue) => demoVue.todos.map(_.content) 113 | ) 114 | ) 115 | .setFilters(new StringDictionary[js.Function]: 116 | val reverse: js.Function1[js.Any, String] = 117 | _.toString.reverse 118 | val wrap: js.Function3[js.Any, String, String, String] = 119 | (value: js.Any, begin: String, end: String) => begin + value.toString + end 120 | val extract: js.Function2[js.UndefOr[js.Array[js.Dynamic]], String, js.UndefOr[js.Array[js.Dynamic]]] = 121 | (array, field) => array.map(_.map(_.selectDynamic(field))) 122 | ) 123 | 124 | val demo = new VueClass(demoOpt).value 125 | 126 | demo.$watch("title", (_: demo.type, newValue, _) => println("changed " + newValue)) 127 | end main 128 | 129 | object Knowledge: 130 | type Vue = typings.vue.typesVueMod.Vue 131 | 132 | /** We need a custom import because the normal module doesn't include 133 | * a... compiler or something. Seems we're running code at runtime which could have ran at build time. 134 | * 135 | * Discussion at https://github.com/vuejs-templates/webpack/issues/215 136 | */ 137 | @JSImport("vue/dist/vue", JSImport.Namespace) 138 | @js.native 139 | object Vue extends typings.vue.typesVueMod.VueConstructor[typings.vue.typesVueMod.Vue] 140 | 141 | /** Needs the same custom import as above (it's the same object) 142 | * 143 | * In the ScalablyTyped encoding the constructor ended up as an `Instantiable`, which means it has lost its type 144 | * parameters. We recreate it here to avoid casting. 145 | * 146 | * Finally, the shape of the Vue object is hard to rewrite into a Scala class, because the return type is an 147 | * intersection type (can't extend from that). That's what's fixed by the `.value` ceremony 148 | */ 149 | @JSImport("vue/dist/vue", JSImport.Namespace) 150 | @js.native 151 | class VueClass[V <: Vue, Data, Methods, Computed, PropsDef, Props]( 152 | options: ComponentOptions[V, Data, Methods, Computed, PropsDef, Props] 153 | ) extends js.Object 154 | 155 | object VueClass: 156 | @inline implicit class VueClassOps[V <: Vue, Data, Methods, Computed, PropsDef, Props]( 157 | val instance: VueClass[V, Data, Methods, Computed, PropsDef, Props] 158 | ) extends AnyVal: 159 | @inline def value: CombinedVueInstance[V, Data, Methods, Computed, Props] = 160 | instance.asInstanceOf[CombinedVueInstance[V, Data, Methods, Computed, Props]] 161 | end VueClassOps 162 | end VueClass 163 | 164 | def isAccessors[T](t: T): Accessors[T] = 165 | t.asInstanceOf[Accessors[T]] 166 | end Knowledge 167 | --------------------------------------------------------------------------------