├── .circleci
└── config.yml
├── .gitignore
├── .sbtopts
├── .scalafmt.conf
├── LICENSE
├── antd
├── src
│ └── main
│ │ ├── js
│ │ ├── index.css
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── App.scala
│ │ ├── CoordinatedDemo.scala
│ │ └── Main.scala
└── yarn.lock
├── build.sbt
├── custom.webpack.config.js
├── docs
├── antd
│ ├── antd-opt-bundle.js
│ └── index.html
├── downshift
│ ├── downshift-opt-bundle.js
│ └── index.html
├── material-ui
│ ├── 8cb96001ce8b133ffa0460ec825923b0.svg
│ ├── a0185b043dee68a72aebffdcc939e21e.svg
│ ├── index.html
│ └── material-ui-opt-bundle.js
├── nivo
│ ├── index.html
│ └── nivo-opt-bundle.js
├── office-ui-fabric-react
│ ├── index.html
│ └── office-ui-fabric-react-opt-bundle.js
├── react-big-calendar
│ ├── index.html
│ └── react-big-calendar-opt-bundle.js
├── react-dnd
│ ├── index.html
│ └── react-dnd-opt-bundle.js
├── react-i18n
│ ├── index.html
│ └── react-i18n-opt-bundle.js
├── react-leaflet
│ ├── 2273e3d8ad9264b7daa5bdbf8e6b47f8.png
│ ├── 4f0283c6ce28e888000e978e537a6a56.png
│ ├── a6137456ed160d7606981aa57c559898.png
│ ├── index.html
│ └── react-leaflet-opt-bundle.js
├── react-markdown
│ ├── docs
│ │ └── README.md
│ ├── index.html
│ └── react-markdown-opt-bundle.js
├── react-mobx
│ ├── index.html
│ └── react-mobx-opt-bundle.js
├── react-redux
│ ├── index.html
│ └── react-redux-opt-bundle.js
├── react-router-dom
│ ├── index.html
│ └── react-router-dom-opt-bundle.js
├── react-slick
│ ├── index.html
│ └── react-slick-opt-bundle.js
├── react-window
│ ├── index.html
│ └── react-window-opt-bundle.js
├── semantic-ui-react-kitchensink
│ ├── index.html
│ └── semantic-ui-react-kitchensink-opt-bundle.js
└── storybook-react
│ ├── favicon.ico
│ ├── iframe.html
│ ├── index.html
│ ├── main.25f83ce417c194d85b6d.bundle.js
│ ├── main.311a062f112ecb6ac41a.bundle.js
│ ├── main.311a062f112ecb6ac41a.bundle.js.map
│ ├── runtime~main.286b69a873d49f2f199c.bundle.js
│ ├── runtime~main.311a062f112ecb6ac41a.bundle.js
│ ├── runtime~main.311a062f112ecb6ac41a.bundle.js.map
│ ├── sb_dll
│ ├── storybook_ui-manifest.json
│ ├── storybook_ui_dll.LICENCE
│ └── storybook_ui_dll.js
│ ├── vendors~main.311a062f112ecb6ac41a.bundle.js
│ ├── vendors~main.311a062f112ecb6ac41a.bundle.js.LICENSE.txt
│ ├── vendors~main.311a062f112ecb6ac41a.bundle.js.map
│ └── vendors~main.eee964e324deca5095da.bundle.js
├── downshift
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── Demo.scala
└── yarn.lock
├── material-ui
├── src
│ └── main
│ │ ├── js
│ │ ├── google.svg
│ │ ├── index.html
│ │ └── logo.svg
│ │ └── scala
│ │ └── demo
│ │ ├── Demo.scala
│ │ ├── StyleBuilder.scala
│ │ ├── album
│ │ └── Album.scala
│ │ ├── button
│ │ └── Button.scala
│ │ ├── components
│ │ └── AppTheme.scala
│ │ ├── customization
│ │ ├── DarkTheme.scala
│ │ ├── Palette.scala
│ │ └── WithTheme.scala
│ │ ├── dashboard
│ │ ├── Dashboard.scala
│ │ ├── ListItems.scala
│ │ ├── SimpleLineChart.scala
│ │ └── SimpleTable.scala
│ │ ├── login
│ │ ├── Login.scala
│ │ └── Styles.scala
│ │ └── signin
│ │ └── SignIn.scala
└── yarn.lock
├── nivo
├── src
│ └── main
│ │ ├── js
│ │ ├── data.json
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── App.scala
│ │ └── Main.scala
└── yarn.lock
├── office-ui-fabric-react
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── Demo.scala
└── yarn.lock
├── project
├── ScalacOptions.scala
├── build.properties
├── plugins.sbt
└── scalafmt.sbt
├── react-big-calendar
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── Main.scala
└── yarn.lock
├── react-dnd
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── App.scala
│ │ └── Main.scala
└── yarn.lock
├── react-i18n
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── App.scala
│ │ └── I18n.scala
└── yarn.lock
├── react-leaflet
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── App.scala
│ │ └── Main.scala
└── yarn.lock
├── react-markdown
├── src
│ └── main
│ │ ├── js
│ │ ├── docs
│ │ │ └── README.md
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── DocPage.scala
│ │ └── Main.scala
└── yarn.lock
├── react-mobx
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── Main.scala
│ │ ├── ObserverComponent.scala
│ │ ├── PeopleStore.scala
│ │ ├── TodoList.scala
│ │ ├── TodoStore.scala
│ │ └── TodoView.scala
└── yarn.lock
├── react-native
├── App.js
├── app.json
├── assets
│ ├── icon.png
│ └── splash.png
├── babel.config.js
├── fastopt-noparse.js
├── metro.config.js
├── package.json
├── src
│ └── main
│ │ └── scala
│ │ └── hello
│ │ └── world
│ │ ├── Antd.scala
│ │ ├── App.scala
│ │ ├── Home.scala
│ │ ├── LoadFonts.scala
│ │ ├── Main.scala
│ │ ├── ReactRouter.scala
│ │ ├── Styles.scala
│ │ └── Topic.scala
└── yarn.lock
├── react-redux
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ ├── Main.scala
│ │ ├── advanced
│ │ ├── Expense.scala
│ │ ├── ExpenseAction.scala
│ │ ├── ExpenseContainer.scala
│ │ ├── ExpenseReducer.scala
│ │ └── ExpenseState.scala
│ │ └── basic
│ │ ├── CakeAction.scala
│ │ ├── CakeContainer.scala
│ │ └── CakeReducer.scala
└── yarn.lock
├── react-router-dom
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── App.scala
└── yarn.lock
├── react-slick
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── Main.scala
└── yarn.lock
├── react-window
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── Main.scala
└── yarn.lock
├── readme.md
├── semantic-ui-react-kitchensink
├── readme.md
├── src
│ └── main
│ │ ├── js
│ │ └── index.html
│ │ └── scala
│ │ └── demo
│ │ └── App.scala
└── yarn.lock
└── storybook-react
├── .storybook-dist
├── config.js
└── webpack.config.js
├── .storybook
├── config.js
└── webpack.config.js
├── package.json
├── src
└── main
│ └── scala
│ └── demo
│ └── 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 | - v2-dependencies-{{ checksum "build.sbt" }}
30 | # fallback to using the latest cache if no exact match is found
31 | - v2-dependencies-
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: v2-dependencies--{{ 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 | out/
12 | .bsp/
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/antd/src/main/js/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
7 | .App-title, .App-intro {
8 | text-align: center;
9 | }
10 |
11 | div.block {
12 | color: #fff;
13 | margin-top: 8px;
14 | margin-bottom: 8px;
15 | padding-top: 16px;
16 | padding-bottom: 16px;
17 | text-align: center;
18 | }
19 |
20 | div.blue1 {
21 | background: rgba(0, 160, 233, 0.7);
22 | }
23 |
24 | div.blue2 {
25 | background: #00a0e9;
26 | }
27 |
28 | section {
29 | margin-top: 20px;
30 | }
31 |
--------------------------------------------------------------------------------
/antd/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Antd-scalajs-react demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/antd/src/main/scala/demo/CoordinatedDemo.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import org.scalablytyped.runtime.StringDictionary
6 | import typings.antd.antdStrings
7 | import typings.antd.components.*
8 | import typings.antd.libFormFormMod.useForm
9 | import typings.antd.libGridColMod.ColProps
10 | import typings.rcFieldForm.esInterfaceMod.BaseRule
11 | import typings.std.global.console
12 |
13 | object CoordinatedDemo:
14 | val component = ScalaFnComponent[String] { noteTitle =>
15 | val form = useForm().head
16 | Form
17 | .form(form)
18 | .labelCol(ColProps().setSpan(5))
19 | .wrapperCol(ColProps().setSpan(12))
20 | .onFinish(store => Callback(console.log("Received values of form: ", store)))(
21 | Form.Item
22 | .label(noteTitle)
23 | .name("note")
24 | .rulesVarargs(BaseRule().setRequired(true).setMessage("Please input your note!"))(
25 | Input()
26 | ),
27 | Form.Item
28 | .label("Gender")
29 | .name("gender")
30 | .rulesVarargs(BaseRule().setRequired(true).setMessage("Please select your gender!'"))(
31 | Select[String]()
32 | .placeholder("Select a option and change input text above")
33 | .onChange { (value, _) =>
34 | Callback(
35 | form.setFieldsValue(
36 | StringDictionary(
37 | "gender" -> value,
38 | "note" -> s"Hi, ${if value == "male" then "man" else "lady"}!"
39 | )
40 | )
41 | )
42 | }(
43 | Select.Option(value = "male")("Male"),
44 | Select.Option(value = "female")("Female")
45 | )
46 | ),
47 | Form.Item.wrapperCol(ColProps().setSpan(12).setOffset(5))(
48 | Button.`type`(antdStrings.primary).htmlType(antdStrings.submit)("Submit")
49 | )
50 | )
51 | }
52 | end CoordinatedDemo
53 |
--------------------------------------------------------------------------------
/antd/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom
4 |
5 | import scala.scalajs.js
6 | import scala.scalajs.js.annotation.JSImport
7 |
8 | @JSImport("./index.css", JSImport.Namespace)
9 | @js.native
10 | object IndexCSS extends js.Object
11 |
12 | @main
13 | def main: Unit =
14 | IndexCSS
15 | App.component().renderIntoDOM(dom.document.getElementById("container"))
16 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | import java.nio.file.Files
2 | import java.nio.file.StandardCopyOption.REPLACE_EXISTING
3 | import scala.sys.process.Process
4 |
5 | Global / excludeLintKeys += webpackExtraArgs
6 |
7 | Global / onLoad := {
8 | println("""*
9 | |* Welcome to ScalablyTyped demos!
10 | |*
11 | |* These demos demonstrate how to use third party react components with Scalajs-react.
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 | |*
17 | |* If you import this the first time in a memory-constrained context like an IDE import, it'll take MUCH longer.
18 | |*""".stripMargin)
19 | (Global / onLoad).value
20 | }
21 |
22 |
23 | Global / stRemoteCache := RemoteCache.S3Aws(bucket = "scalablytyped-demos", region = "eu-central-1", prefix = Some("st-cache"))
24 |
25 | // Uncomment if you want to remove debug output
26 | //Global / stQuiet := true
27 |
28 | /**
29 | * Custom task to start demo with webpack-dev-server, use as `/start`.
30 | * Just `start` also works, and starts all frontend demos
31 | *
32 | * After that, the incantation is this to watch and compile on change:
33 | * `~/fastOptJS::webpack`
34 | */
35 | lazy val start = TaskKey[Unit]("start")
36 |
37 | /** Say just `dist` or `/dist` to make a production bundle in
38 | * `docs` for github publishing
39 | */
40 | lazy val dist = TaskKey[File]("dist")
41 |
42 | lazy val baseSettings: Project => Project =
43 | _.enablePlugins(ScalaJSPlugin)
44 | .settings(
45 | version := "0.1-SNAPSHOT",
46 | scalaVersion := "3.3.0",
47 | scalacOptions ++= ScalacOptions.flags,
48 | scalaJSUseMainModuleInitializer := true,
49 | /* disabled because it somehow triggers many warnings */
50 | scalaJSLinkerConfig := scalaJSLinkerConfig.value.withSourceMap(false)
51 | )
52 |
53 | lazy val `react-mobx` =
54 | project
55 | .enablePlugins(ScalablyTypedConverterPlugin)
56 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
57 | .settings(
58 | useYarn := true,
59 | webpackDevServerPort := 8001,
60 | stFlavour := Flavour.ScalajsReact,
61 | Compile / npmDependencies ++= Seq(
62 | "mobx" -> "5.15.4",
63 | "mobx-react" -> "6.2.2"
64 | )
65 | )
66 |
67 | lazy val `react-slick` =
68 | project
69 | .enablePlugins(ScalablyTypedConverterPlugin)
70 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
71 | .settings(
72 | useYarn := true,
73 | webpackDevServerPort := 8002,
74 | stFlavour := Flavour.ScalajsReact,
75 | Compile / npmDependencies ++= Seq(
76 | "react-slick" -> "0.23",
77 | "@types/react-slick" -> "0.23.4"
78 | )
79 | )
80 |
81 | lazy val `react-big-calendar` =
82 | project
83 | .enablePlugins(ScalablyTypedConverterPlugin)
84 | .configure(baseSettings, browserProject, withCssLoading, reactNpmDeps, bundlerSettings)
85 | .settings(
86 | useYarn := true,
87 | webpackDevServerPort := 8003,
88 | stFlavour := Flavour.ScalajsReact,
89 | Compile / npmDependencies ++= Seq(
90 | "moment" -> "2.24.0",
91 | "react-big-calendar" -> "0.24.4",
92 | "@types/react-big-calendar" -> "0.24.0"
93 | )
94 | )
95 |
96 | lazy val `semantic-ui-react-kitchensink` = project
97 | .enablePlugins(ScalablyTypedConverterPlugin)
98 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
99 | .settings(
100 | useYarn := true,
101 | webpackDevServerPort := 8004,
102 | stFlavour := Flavour.ScalajsReact,
103 | stReactEnableTreeShaking := Selection.All,
104 | Compile / npmDependencies ++= Seq(
105 | "semantic-ui-react" -> "0.88.2"
106 | )
107 | )
108 |
109 | ///** Note: This can't use scalajs-bundler (at least I don't know how),
110 | // * so we run yarn ourselves with an external package.json.
111 | // */
112 | //lazy val `storybook-react` = project
113 | // .enablePlugins(ScalablyTypedConverterExternalNpmPlugin)
114 | // .configure(baseSettings)
115 | // .settings(
116 | // scalaJSLinkerConfig := scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule),
117 | // /* ScalablyTypedConverterExternalNpmPlugin requires that we define how to install node dependencies and where they are */
118 | // externalNpm := {
119 | // if (scala.util.Properties.isWin) Process("yarn", baseDirectory.value).run()
120 | // else Process("bash -ci 'yarn'", baseDirectory.value).run()
121 | // baseDirectory.value
122 | // },
123 | // stFlavour := Flavour.ScalajsReact,
124 | // /** This is not suitable for development, but effective for demo.
125 | // * Run `yarn storybook` commands yourself, and run `~storybook-react/fastOptJS` from sbt
126 | // */
127 | // start := {
128 | // (Compile / fastOptJS).value
129 | // Process("yarn storybook", baseDirectory.value).run()
130 | // },
131 | // dist := {
132 | // val distFolder = (ThisBuild / baseDirectory).value / "docs" / moduleName.value
133 | // (Compile / fullOptJS).value
134 | // if (scala.util.Properties.isWin) Process("yarn dist", baseDirectory.value).run()
135 | // else Process("bash -ci 'yarn dist'", baseDirectory.value).run()
136 | // distFolder
137 | // }
138 | // )
139 |
140 | lazy val antd =
141 | project
142 | .enablePlugins(ScalablyTypedConverterPlugin)
143 | .configure(baseSettings, browserProject, withCssLoading, reactNpmDeps, bundlerSettings)
144 | .settings(
145 | useYarn := true,
146 | webpackDevServerPort := 8006,
147 | stFlavour := Flavour.ScalajsReact,
148 | Compile / npmDependencies ++= Seq("antd" -> "4.5.1")
149 | )
150 |
151 | lazy val `react-router-dom` =
152 | project
153 | .enablePlugins(ScalablyTypedConverterPlugin)
154 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
155 | .settings(
156 | useYarn := true,
157 | webpackDevServerPort := 8007,
158 | stFlavour := Flavour.ScalajsReact,
159 | Compile / npmDependencies ++= Seq(
160 | "react-router-dom" -> "5.1.2",
161 | "@types/react-router-dom" -> "5.1.2" // note 5.1.4 did weird things to the Link component
162 | )
163 | )
164 |
165 | lazy val `material-ui` =
166 | project
167 | .enablePlugins(ScalablyTypedConverterPlugin)
168 | .configure(baseSettings, browserProject, reactNpmDeps, withCssLoading, bundlerSettings)
169 | .settings(
170 | useYarn := true,
171 | webpackDevServerPort := 8008,
172 | stFlavour := Flavour.ScalajsReact,
173 | stReactEnableTreeShaking := Selection.All,
174 | Compile / npmDependencies ++= Seq(
175 | "@material-ui/core" -> "3.9.4", // note: version 4 is not supported yet
176 | "@material-ui/styles" -> "3.0.0-alpha.10", // note: version 4 is not supported yet
177 | "@material-ui/icons" -> "3.0.2",
178 | "recharts" -> "1.8.5",
179 | "@types/recharts" -> "1.8.10",
180 | "@types/classnames" -> "2.2.10",
181 | "react-router-dom" -> "5.1.2",
182 | "@types/react-router-dom" -> "5.1.2"
183 | )
184 | )
185 |
186 | lazy val `react-leaflet` = project
187 | .enablePlugins(ScalablyTypedConverterPlugin)
188 | .configure(baseSettings, browserProject, reactNpmDeps, withCssLoading, bundlerSettings)
189 | .settings(
190 | useYarn := true,
191 | webpackDevServerPort := 8009,
192 | stFlavour := Flavour.ScalajsReact,
193 | Compile / npmDependencies ++= Seq(
194 | "react-leaflet" -> "2.6.3",
195 | "@types/react-leaflet" -> "2.5.1",
196 | "leaflet" -> "1.6.0"
197 | )
198 | )
199 |
200 | lazy val `office-ui-fabric-react` = project
201 | .enablePlugins(ScalablyTypedConverterPlugin)
202 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
203 | .settings(
204 | useYarn := true,
205 | webpackDevServerPort := 8010,
206 | stFlavour := Flavour.ScalajsReact,
207 | stReactEnableTreeShaking := Selection.All,
208 | Compile / npmDependencies ++= Seq(
209 | "office-ui-fabric-react" -> "7.107.1"
210 | )
211 | )
212 |
213 | lazy val `react-dnd` = project
214 | .enablePlugins(ScalablyTypedConverterPlugin)
215 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
216 | .settings(
217 | useYarn := true,
218 | webpackDevServerPort := 8011,
219 | stFlavour := Flavour.ScalajsReact,
220 | Compile / npmDependencies ++= Seq(
221 | "react-dnd" -> "11.1.3",
222 | "react-dnd-html5-backend" -> "11.1.3"
223 | )
224 | )
225 |
226 | lazy val `react-i18n` = project
227 | .enablePlugins(ScalablyTypedConverterPlugin)
228 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
229 | .settings(
230 | useYarn := true,
231 | webpackDevServerPort := 8012,
232 | stFlavour := Flavour.ScalajsReact,
233 | Compile / npmDependencies ++= Seq(
234 | "i18next" -> "19.5.2",
235 | "i18next-browser-languagedetector" -> "5.0.0",
236 | "react-i18next" -> "11.7.0"
237 | )
238 | )
239 |
240 | lazy val `nivo` = project
241 | .enablePlugins(ScalablyTypedConverterPlugin)
242 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
243 | .settings(
244 | useYarn := true,
245 | webpackDevServerPort := 8013,
246 | stFlavour := Flavour.ScalajsReact,
247 | Compile / npmDependencies ++= Seq(
248 | "@nivo/line" -> "0.62.0"
249 | )
250 | )
251 |
252 | lazy val downshift = project
253 | .enablePlugins(ScalablyTypedConverterPlugin)
254 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
255 | .settings(
256 | useYarn := true,
257 | webpackDevServerPort := 8014,
258 | stFlavour := Flavour.ScalajsReact,
259 | Compile / npmDependencies ++= Seq(
260 | "downshift" -> "6.0.5"
261 | )
262 | )
263 |
264 | lazy val `react-redux` = project
265 | .enablePlugins(ScalablyTypedConverterPlugin)
266 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
267 | .settings(
268 | useYarn := true,
269 | webpackDevServerPort := 8015,
270 | stFlavour := Flavour.ScalajsReact,
271 | stReactEnableTreeShaking := Selection.All,
272 | Compile / npmDependencies ++= Seq(
273 | "react-redux" -> "7.1",
274 | "redux-devtools-extension" -> "2.13.8",
275 | "@types/react-redux" -> "7.1.5"
276 | ),
277 | libraryDependencies += ("org.scala-js" %%% "scalajs-java-securerandom" % "1.0.0").cross(CrossVersion.for3Use2_13)
278 | )
279 |
280 | lazy val `react-window` = project
281 | .enablePlugins(ScalablyTypedConverterPlugin)
282 | .configure(baseSettings, browserProject, reactNpmDeps, bundlerSettings)
283 | .settings(
284 | useYarn := true,
285 | webpackDevServerPort := 8016,
286 | stFlavour := Flavour.ScalajsReact,
287 | stReactEnableTreeShaking := Selection.All,
288 | Compile / npmDependencies ++= Seq(
289 | "react-window" -> "1.8.6",
290 | "@types/react-window" -> "1.8.2",
291 | "react-virtualized-auto-sizer" -> "1.0.2", // as recommended by react-window
292 | "@types/react-virtualized-auto-sizer" -> "1.0.0",
293 | )
294 | )
295 |
296 | lazy val `react-markdown` = project
297 | .enablePlugins(ScalablyTypedConverterPlugin)
298 | .configure(baseSettings, withCssLoading, browserProject, reactNpmDeps, bundlerSettings)
299 | .settings(
300 | useYarn := true,
301 | webpackDevServerPort := 8017,
302 | stFlavour := Flavour.ScalajsReact,
303 | Compile / npmDependencies ++= Seq(
304 | "react-markdown"-> "^5.0.3",
305 | "react-syntax-highlighter"-> "^15.4.3",
306 | "@types/react-syntax-highlighter"-> "^13.5.0"
307 | )
308 | )
309 |
310 | /** Note: This can't use scalajs-bundler (at least I don't know how),
311 | * so we run yarn ourselves with an external package.json.
312 | */
313 | lazy val `react-native` = project
314 | .enablePlugins(ScalablyTypedConverterExternalNpmPlugin)
315 | .configure(baseSettings)
316 | .settings(
317 | scalaJSLinkerConfig := scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule),
318 | scalaJSUseMainModuleInitializer := false,
319 | /* ScalablyTypedConverterExternalNpmPlugin requires that we define how to install node dependencies and where they are */
320 | externalNpm := {
321 | if (scala.util.Properties.isWin) Process("yarn", baseDirectory.value).run()
322 | else Process("bash -ci 'yarn'", baseDirectory.value).run()
323 | baseDirectory.value
324 | },
325 | stFlavour := Flavour.ScalajsReact,
326 | stStdlib := List("es5"),
327 | run := {
328 | (Compile / fastOptJS).value
329 | Process("expo start", baseDirectory.value).!
330 | }
331 | )
332 |
333 | // specify versions for all of reacts dependencies to compile less since we have many demos here
334 | lazy val reactNpmDeps: Project => Project =
335 | _.settings(
336 | stTypescriptVersion := "3.9.3",
337 | Compile / npmDependencies ++= Seq(
338 | "react" -> "16.13.1",
339 | "react-dom" -> "16.13.1",
340 | "@types/react" -> "16.9.42",
341 | "@types/react-dom" -> "16.9.8",
342 | "csstype" -> "2.6.11",
343 | "@types/prop-types" -> "15.7.3"
344 | )
345 | )
346 |
347 | lazy val bundlerSettings: Project => Project =
348 | _.settings(
349 | webpackCliVersion := "4.10.0",
350 | webpack / version := "5.88.2",
351 | Compile / fastOptJS / webpackExtraArgs += "--mode=development",
352 | Compile / fullOptJS / webpackExtraArgs += "--mode=production",
353 | Compile / fastOptJS / webpackDevServerExtraArgs += "--mode=development",
354 | Compile / fullOptJS / webpackDevServerExtraArgs += "--mode=production"
355 | )
356 |
357 | lazy val withCssLoading: Project => Project =
358 | _.settings(
359 | /* custom webpack file to include css */
360 | webpackConfigFile := Some((ThisBuild / baseDirectory).value / "custom.webpack.config.js"),
361 | Compile / npmDevDependencies ++= Seq(
362 | "webpack-merge" -> "5.9.0",
363 | "css-loader" -> "6.8.1",
364 | "style-loader" -> "3.3.3",
365 | "file-loader" -> "6.2.0",
366 | "url-loader" -> "4.1.1"
367 | )
368 | )
369 |
370 | /**
371 | * Implement the `start` and `dist` tasks defined above.
372 | * Most of this is really just to copy the index.html file around.
373 | */
374 | lazy val browserProject: Project => Project =
375 | _.settings(
376 | start := {
377 | (Compile / fastOptJS / startWebpackDevServer).value
378 | },
379 | dist := {
380 | val artifacts = (Compile / fullOptJS / webpack).value
381 | val artifactFolder = (Compile / fullOptJS / crossTarget).value
382 | val distFolder = (ThisBuild / baseDirectory).value / "docs" / moduleName.value
383 |
384 | distFolder.mkdirs()
385 | artifacts.foreach { artifact =>
386 | val target = artifact.data.relativeTo(artifactFolder) match {
387 | case None => distFolder / artifact.data.name
388 | case Some(relFile) => distFolder / relFile.toString
389 | }
390 |
391 | Files.copy(artifact.data.toPath, target.toPath, REPLACE_EXISTING)
392 | }
393 |
394 | val indexFrom = baseDirectory.value / "src/main/js/index.html"
395 | val indexTo = distFolder / "index.html"
396 |
397 | val indexPatchedContent = {
398 | import collection.JavaConverters._
399 | Files
400 | .readAllLines(indexFrom.toPath, IO.utf8)
401 | .asScala
402 | .map(_.replaceAllLiterally("-fastopt-", "-opt-"))
403 | .mkString("\n")
404 | }
405 |
406 | Files.write(indexTo.toPath, indexPatchedContent.getBytes(IO.utf8))
407 | distFolder
408 | }
409 | )
410 |
--------------------------------------------------------------------------------
/custom.webpack.config.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge').merge;
2 | var generated = require('./scalajs.webpack.config');
3 |
4 | var local = {
5 | module: {
6 | rules: [
7 | {
8 | test: /\.css$/,
9 | use: ['style-loader', 'css-loader']
10 | },
11 | {
12 | test: /\.(ttf|eot|woff|png|glb|svg|md)$/,
13 | use: 'file-loader'
14 | },
15 | {
16 | test: /\.(eot)$/,
17 | use: 'url-loader'
18 | }
19 | ]
20 | }
21 | };
22 |
23 | module.exports = merge(generated, local);
24 |
--------------------------------------------------------------------------------
/docs/antd/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Antd-scalajs-react demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/downshift/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Downshift demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/material-ui/8cb96001ce8b133ffa0460ec825923b0.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/docs/material-ui/a0185b043dee68a72aebffdcc939e21e.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/material-ui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Material-ui demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/nivo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Nivo demo
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/office-ui-fabric-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | office-ui-fabric-react demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/react-big-calendar/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-big-calendar demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/react-dnd/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-dnd demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/react-i18n/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React i18n demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/react-leaflet/2273e3d8ad9264b7daa5bdbf8e6b47f8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScalablyTyped/ScalaJsReactDemos/ee2c2dfad18ffe29f7ad092a20ee5aa4c0eca841/docs/react-leaflet/2273e3d8ad9264b7daa5bdbf8e6b47f8.png
--------------------------------------------------------------------------------
/docs/react-leaflet/4f0283c6ce28e888000e978e537a6a56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScalablyTyped/ScalaJsReactDemos/ee2c2dfad18ffe29f7ad092a20ee5aa4c0eca841/docs/react-leaflet/4f0283c6ce28e888000e978e537a6a56.png
--------------------------------------------------------------------------------
/docs/react-leaflet/a6137456ed160d7606981aa57c559898.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScalablyTyped/ScalaJsReactDemos/ee2c2dfad18ffe29f7ad092a20ee5aa4c0eca841/docs/react-leaflet/a6137456ed160d7606981aa57c559898.png
--------------------------------------------------------------------------------
/docs/react-leaflet/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-leaflet demo
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/react-markdown/docs/README.md:
--------------------------------------------------------------------------------
1 | # Working with objects
2 |
3 | Javascript is remarkably flexible. When we integrate with arbitrary Javascript code in Scala.js, we need a very flexible
4 | encoding to tag along. The encoding chosen for ScalablyTyped is the result of years of experimentation, and has
5 | a much more dynamic feeling than what you may be used to.
6 |
7 | Let's start with an example of a type definition we want to use:
8 |
9 | ```scala
10 | @js.native
11 | trait Point extends StObject {
12 |
13 | var x: Double = js.native
14 |
15 | var y: Double = js.native
16 | }
17 | object Point {
18 |
19 | @scala.inline
20 | def apply(x: Double, y: Double): Point = {
21 | val __obj = js.Dynamic.literal(x = x.asInstanceOf[js.Any], y = y.asInstanceOf[js.Any])
22 | __obj.asInstanceOf[Point]
23 | }
24 |
25 | @scala.inline
26 | implicit class PointMutableBuilder[Self <: Point] (val x: Self) extends AnyVal {
27 |
28 | @scala.inline
29 | def setX(value: Double): Self = StObject.set(x, "x", value.asInstanceOf[js.Any])
30 |
31 | @scala.inline
32 | def setY(value: Double): Self = StObject.set(x, "y", value.asInstanceOf[js.Any])
33 | }
34 | }
35 | ```
36 |
37 | We notice several things:
38 | - it's a `@js.native` trait, so we cannot `new` it ourselves. This can be [`changed`](conversion-options.md#stenablescalajsdefined), but it's not recommended.
39 | - it has two required members (`x` and `y`). Optional members would typically be wrapped in `js.UndefOr`
40 | - it has an `object` with syntax to help us work with it
41 | - the entire syntax is built on mutability. It's Javascript, after all. more on that further down
42 |
43 | ### Basic usage
44 |
45 | ```scala
46 | // At construction time we need to supply all required parameters
47 | val p = Point(x = 1,y = 2)
48 |
49 | // we can mutate what we have
50 | // this is equivalent to typescript `p.x = 3
51 | val p2 = p.setX(3)
52 |
53 | // or we can duplicate and then mutate.
54 | // this is equivalent to typescript `const p2 = {...p, x: 3}
55 | val p3 = p.duplicate.setX(3)
56 |
57 | // we can combine with other javascript objects.
58 | // this is equivalent to javascript `const p3 = {...p, {}}`
59 | val p4: Point with TickOptions = p.combineWith(TickOptions())
60 |
61 | // fallback, if the type definitions are wrong or for any other reason you can break the contract
62 | val p5: p.duplicate.set("x", "foo")
63 |
64 | // you can also set any other property
65 | val p6: p.duplicate.set("x2", "foo")
66 | ```
67 |
--------------------------------------------------------------------------------
/docs/react-markdown/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-markdown demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/react-mobx/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/react-redux/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-redux demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/react-router-dom/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-router-dom-slinky demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/react-slick/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-slick demo
6 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/react-window/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-window demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/semantic-ui-react-kitchensink/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Semantic-ui-react demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/storybook-react/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScalablyTyped/ScalaJsReactDemos/ee2c2dfad18ffe29f7ad092a20ee5aa4c0eca841/docs/storybook-react/favicon.ico
--------------------------------------------------------------------------------
/docs/storybook-react/iframe.html:
--------------------------------------------------------------------------------
1 | StorybookNo Preview
Sorry, but you either have no stories or none are selected somehow.
- Please check the Storybook config.
- Try reloading the page.
If the problem persists, check the browser console, or the terminal you've run Storybook from.
--------------------------------------------------------------------------------
/docs/storybook-react/index.html:
--------------------------------------------------------------------------------
1 | Storybook
--------------------------------------------------------------------------------
/docs/storybook-react/main.25f83ce417c194d85b6d.bundle.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{403:function(n,o,p){p(404),n.exports=p(547)},466:function(n,o){}},[[403,1,2]]]);
--------------------------------------------------------------------------------
/docs/storybook-react/main.311a062f112ecb6ac41a.bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.311a062f112ecb6ac41a.bundle.js","sources":["webpack:///main.311a062f112ecb6ac41a.bundle.js"],"mappings":"AAAA","sourceRoot":""}
--------------------------------------------------------------------------------
/docs/storybook-react/runtime~main.286b69a873d49f2f199c.bundle.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,l,f=r[0],i=r[1],a=r[2],c=0,s=[];c
40 | *
41 | * Copyright (c) 2014-2017, Jon Schlinkert.
42 | * Released under the MIT License.
43 | */
44 |
45 | /**
46 | * @license
47 | * Lodash
48 | * Copyright OpenJS Foundation and other contributors
49 | * Released under MIT license
50 | * Based on Underscore.js 1.8.3
51 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
52 | */
53 |
54 | /** @license React v0.18.0
55 | * scheduler.production.min.js
56 | *
57 | * Copyright (c) Facebook, Inc. and its affiliates.
58 | *
59 | * This source code is licensed under the MIT license found in the
60 | * LICENSE file in the root directory of this source tree.
61 | */
62 |
63 | /** @license React v16.12.0
64 | * react-dom.production.min.js
65 | *
66 | * Copyright (c) Facebook, Inc. and its affiliates.
67 | *
68 | * This source code is licensed under the MIT license found in the
69 | * LICENSE file in the root directory of this source tree.
70 | */
71 |
72 | /** @license React v16.12.0
73 | * react-is.production.min.js
74 | *
75 | * Copyright (c) Facebook, Inc. and its affiliates.
76 | *
77 | * This source code is licensed under the MIT license found in the
78 | * LICENSE file in the root directory of this source tree.
79 | */
80 |
81 | /** @license React v16.12.0
82 | * react.production.min.js
83 | *
84 | * Copyright (c) Facebook, Inc. and its affiliates.
85 | *
86 | * This source code is licensed under the MIT license found in the
87 | * LICENSE file in the root directory of this source tree.
88 | */
89 |
90 | /**!
91 | * @fileOverview Kickass library to create and place poppers near their reference elements.
92 | * @version 1.16.1
93 | * @license
94 | * Copyright (c) 2016 Federico Zivolo and contributors
95 | *
96 | * Permission is hereby granted, free of charge, to any person obtaining a copy
97 | * of this software and associated documentation files (the "Software"), to deal
98 | * in the Software without restriction, including without limitation the rights
99 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100 | * copies of the Software, and to permit persons to whom the Software is
101 | * furnished to do so, subject to the following conditions:
102 | *
103 | * The above copyright notice and this permission notice shall be included in all
104 | * copies or substantial portions of the Software.
105 | *
106 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
107 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
108 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
109 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
110 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
111 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
112 | * SOFTWARE.
113 | */
114 |
--------------------------------------------------------------------------------
/docs/storybook-react/vendors~main.311a062f112ecb6ac41a.bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /*!
8 | * https://github.com/es-shims/es5-shim
9 | * @license es5-shim Copyright 2009-2020 by contributors, MIT License
10 | * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
11 | */
12 |
13 | /*!
14 | * https://github.com/paulmillr/es6-shim
15 | * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
16 | * and contributors, MIT License
17 | * es6-shim: v0.35.4
18 | * see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
19 | * Details and documentation:
20 | * https://github.com/paulmillr/es6-shim/
21 | */
22 |
23 | /*!
24 | * is-plain-object
25 | *
26 | * Copyright (c) 2014-2017, Jon Schlinkert.
27 | * Released under the MIT License.
28 | */
29 |
30 | /*!
31 | * isobject
32 | *
33 | * Copyright (c) 2014-2017, Jon Schlinkert.
34 | * Released under the MIT License.
35 | */
36 |
37 | /** @license React v0.19.1
38 | * scheduler.production.min.js
39 | *
40 | * Copyright (c) Facebook, Inc. and its affiliates.
41 | *
42 | * This source code is licensed under the MIT license found in the
43 | * LICENSE file in the root directory of this source tree.
44 | */
45 |
46 | /** @license React v16.13.1
47 | * react-dom.production.min.js
48 | *
49 | * Copyright (c) Facebook, Inc. and its affiliates.
50 | *
51 | * This source code is licensed under the MIT license found in the
52 | * LICENSE file in the root directory of this source tree.
53 | */
54 |
55 | /** @license React v16.13.1
56 | * react.production.min.js
57 | *
58 | * Copyright (c) Facebook, Inc. and its affiliates.
59 | *
60 | * This source code is licensed under the MIT license found in the
61 | * LICENSE file in the root directory of this source tree.
62 | */
63 |
64 | //! stable.js 0.1.8, https://github.com/Two-Screen/stable
65 |
66 | //! © 2018 Angry Bytes and contributors. MIT licensed.
67 |
--------------------------------------------------------------------------------
/docs/storybook-react/vendors~main.311a062f112ecb6ac41a.bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"vendors~main.311a062f112ecb6ac41a.bundle.js","sources":["webpack:///vendors~main.311a062f112ecb6ac41a.bundle.js"],"mappings":";AAAA","sourceRoot":""}
--------------------------------------------------------------------------------
/downshift/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Downshift demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/downshift/src/main/scala/demo/Demo.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.Implicits.*
4 | import japgolly.scalajs.react.vdom.TagMod
5 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
6 | import org.scalajs.dom
7 | import typings.csstype.csstypeStrings.{bold, none, normal}
8 | import typings.csstype.mod.{DisplayLegacy, NamedColor, OverflowYProperty, PositionProperty}
9 | import typings.downshift.components.Downshift
10 | import typings.downshift.mod.{GetItemPropsOptions, GetPropsCommonOptions, GetRootPropsOptions}
11 | import typings.react.components.*
12 | import typings.react.mod.CSSProperties
13 |
14 | import scala.scalajs.js.|
15 |
16 | // https://codesandbox.io/s/github/kentcdodds/downshift-examples/tree/master/?module=/src/ordered-examples/01-basic-autocomplete.js&moduleview=1&file=/src/downshift/ordered-examples/00-get-root-props-example.js
17 | def asOpt[T](t: T | Null): Option[T] = Option(t.asInstanceOf[T])
18 |
19 | val menuStyles = CSSProperties()
20 | .setMaxHeight(80)
21 | .setMaxWidth(300)
22 | .setOverflowY(OverflowYProperty.scroll)
23 | .setBackgroundColor("#eee")
24 | .setPadding(0)
25 | .setListStyle(none)
26 | .setPosition(PositionProperty.relative)
27 |
28 | val comboboxStyles = CSSProperties().setDisplay(DisplayLegacy.`inline-block`).setMarginLeft("5px")
29 |
30 | case class Item(value: String)
31 |
32 | val items = Seq(Item("apple"), Item("pear"), Item("orange"), Item("grape"), Item("banana"))
33 |
34 | val Main = ScalaFnComponent[Unit] { case () =>
35 | Downshift[Item]()
36 | .onChange((selection, _) =>
37 | Callback.alert(asOpt(selection).fold("Selection Cleared")(value => s"You selected ${value.value}"))
38 | )
39 | .itemToString(item => asOpt(item).fold("")(_.value))
40 | .children(ctrl =>
41 | div(
42 | label.unsafeSpread(ctrl.getLabelProps())("Enter a fruit:"),
43 | div
44 | .style(comboboxStyles)
45 | .unsafeSpread(
46 | ctrl.getRootProps(GetRootPropsOptions("refkey"), GetPropsCommonOptions().setSuppressRefError(true))
47 | )(
48 | input.unsafeSpread(ctrl.getInputProps()),
49 | button.unsafeSpread(ctrl.getToggleButtonProps()).`aria-label`("toggle menu")("toggle menu")
50 | ),
51 | ul.unsafeSpread(ctrl.getMenuProps())
52 | .style(menuStyles)(
53 | if !ctrl.isOpen then TagMod.empty
54 | else
55 | TagMod.fromTraversableOnce(
56 | items
57 | .filter(item => asOpt(ctrl.inputValue).fold(false)(inputValue => item.value.contains(inputValue)))
58 | .zipWithIndex
59 | .map { case (item, index) =>
60 | li.unsafeSpread(
61 | ctrl.getItemProps(
62 | GetItemPropsOptions(item)
63 | .setKey(item.value)
64 | .setIndex(index)
65 | .setStyle {
66 | val isSelected = asOpt(ctrl.highlightedIndex).contains(index)
67 | CSSProperties()
68 | .setBackgroundColor(if isSelected then NamedColor.lightgray else NamedColor.white)
69 | .setFontWeight(if isSelected then bold else normal)
70 | }
71 | )
72 | )(item.value)
73 | .build
74 | }
75 | )
76 | )
77 | ).rawNode
78 | )
79 | }
80 |
81 | @main
82 | def main: Unit =
83 | Main().renderIntoDOM(dom.document.getElementById("container"))
84 |
--------------------------------------------------------------------------------
/material-ui/src/main/js/google.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/material-ui/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Material-ui demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/material-ui/src/main/js/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/Demo.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import demo.album.Album
4 | import demo.button.{ButtonTest, SelectDemo, StyledButtonDemo, StyledButtonHooksDemo}
5 | import demo.components.AppTheme
6 | import demo.customization.{DarkTheme, Palette}
7 | import demo.dashboard.Dashboard
8 | import demo.login.Login
9 | import demo.signin.SignIn
10 | import japgolly.scalajs.react.ScalaFnComponent
11 | import japgolly.scalajs.react.vdom.html_<^.*
12 | import org.scalajs.dom
13 | import typings.materialUiCore.components.{List, ListItem, ListItemIcon, ListItemText, ListSubheader, Typography}
14 | import typings.materialUiCore.stylesCreateMuiThemeMod.{Theme, ThemeOptions}
15 | import typings.materialUiCore.stylesCreateTypographyMod.TypographyOptions
16 | import typings.materialUiCore.stylesMod.createMuiTheme
17 | import typings.materialUiCore.typographyTypographyMod.Style
18 | import typings.materialUiIcons.components as Icon
19 | import typings.materialUiStyles.components.ThemeProvider
20 | import typings.react.components.Fragment
21 | import typings.reactRouter.mod.RouteProps
22 | import typings.reactRouterDom.components.{BrowserRouter, Link, Route}
23 |
24 | val theme: Theme = createMuiTheme(
25 | ThemeOptions()
26 | .setTypography(
27 | TypographyOptions().setUseNextVariants(true)
28 | ) // https://v3.material-ui.com/style/typography/#migration-to-typography-v2
29 | )
30 |
31 | /* the production build is deployed at github pages under /material-ui , while dev build is server from root of webpack-dev-server */
32 | val basename = if scala.scalajs.runtime.linkingInfo.productionMode then "/ScalajsReactDemos/material-ui/" else ""
33 |
34 | val Main = ScalaFnComponent[Unit] { case () =>
35 | ThemeProvider(theme)(
36 | BrowserRouter.basename(basename)(
37 | Route(
38 | RouteProps()
39 | .setExact(true)
40 | .setPath("/")
41 | .setRender(_ =>
42 | List(
43 | ListSubheader.inset(true)(""),
44 | Link[String](to = "/dashboard")(
45 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Dashboard"))
46 | ),
47 | Link[String](to = "/album")(
48 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Album"))
49 | ),
50 | Link[String](to = "/signin")(
51 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Sign In"))
52 | ),
53 | Link[String](to = "/login")(
54 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Login"))
55 | ),
56 | Link[String](to = "/button")(
57 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Buttons"))
58 | ),
59 | Link[String](to = "/select")(
60 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Select"))
61 | ),
62 | Link[String](to = "/customization")(
63 | ListItem.button(true)(ListItemIcon(Icon.Assignment()), ListItemText.primary("Customization"))
64 | )
65 | ).rawElement
66 | )
67 | ),
68 | Route(
69 | RouteProps()
70 | .setPath("/dashboard")
71 | .setRender(_ =>
72 | Fragment(
73 | AppTheme.component(
74 | AppTheme.Props(
75 | title = "Dashboard page layout example - Material-UI",
76 | description = "An example layout for creating an albumn.",
77 | hideCredit = true
78 | )
79 | )(Dashboard.component())
80 | ).rawElement
81 | )
82 | ),
83 | Route(
84 | RouteProps()
85 | .setPath("/album")
86 | .setRender(_ =>
87 | Fragment(
88 | AppTheme.component(
89 | AppTheme.Props(
90 | title = "Album page layout - Material-UI",
91 | description = "An example layout for creating an album or gallery."
92 | )
93 | )(Album.component())
94 | ).rawElement
95 | )
96 | ),
97 | Route(
98 | RouteProps()
99 | .setPath("/signin")
100 | .setRender(_ =>
101 | Fragment(
102 | AppTheme.component(
103 | AppTheme.Props(
104 | title = "Sign-in page layout example - Material-UI",
105 | description = "An example layout for creating a sign-in page."
106 | )
107 | )(SignIn.component())
108 | ).rawElement
109 | )
110 | ),
111 | Route(
112 | RouteProps()
113 | .setPath("/login")
114 | .setRender(_ => Login.component().rawElement)
115 | ),
116 | Route(
117 | RouteProps()
118 | .setPath("/button")
119 | .setRender(_ =>
120 | Fragment(
121 | Typography.variant(Style.h4).gutterBottom(true).component("h2")("Buttons"),
122 | ButtonTest.component("dear user"),
123 | StyledButtonDemo.component(),
124 | StyledButtonHooksDemo.component()
125 | ).rawElement
126 | )
127 | ),
128 | Route(
129 | RouteProps()
130 | .setPath("/select")
131 | .setRender(_ =>
132 | Fragment(
133 | Typography.variant(Style.h4).gutterBottom(true).component("h2")("Select"),
134 | SelectDemo.component(scala.List("one", "two", "three"))
135 | ).rawElement
136 | )
137 | ),
138 | Route(
139 | RouteProps()
140 | .setPath("/customization")
141 | .setRender(_ =>
142 | Fragment(
143 | Typography.variant(Style.h4).gutterBottom(true).component("h2")("Customization"),
144 | DarkTheme(),
145 | Palette.component()
146 | ).rawElement
147 | )
148 | )
149 | )
150 | )
151 | }
152 |
153 | @main
154 | def main: Unit =
155 | println("starting")
156 | Main().renderIntoDOM(dom.document.getElementById("container"))
157 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/StyleBuilder.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalablytyped.runtime.StringDictionary
4 | import typings.materialUiStyles.makeStylesMod.StylesHook
5 | import typings.materialUiStyles.mod.makeStyles
6 | import typings.materialUiStyles.withStylesMod.*
7 |
8 | import scala.scalajs.js
9 |
10 | /* This is an example of a scala facade on top of the generated code.
11 | * Note that you can do all this without casting, but type inference is not perfect.
12 | */
13 | object StyleBuilder:
14 | @inline def apply[Theme, Props <: js.Object]: StyleBuilder[Theme, Props] =
15 | new StyleBuilder[Theme, Props](_ => StringDictionary.empty)
16 |
17 | @inline final class StyleBuilder[T, P] private (val f: StyleRulesCallback[T, P, String]) extends AnyVal:
18 | @inline def add(key: String, value: CSSProperties): StyleBuilder[T, P] =
19 | new StyleBuilder[T, P]({ theme =>
20 | val ret = f(theme)
21 | ret.update(key, value)
22 | ret
23 | })
24 |
25 | @inline def add(key: String, withTheme: T => CSSProperties): StyleBuilder[T, P] =
26 | new StyleBuilder[T, P]({ theme =>
27 | val ret = this.f(theme)
28 | ret.update(key, withTheme(theme))
29 | ret
30 | })
31 |
32 | @inline def add(key: String, withThemeProps: (T, P) => CSSProperties): StyleBuilder[T, P] =
33 | new StyleBuilder[T, P]({ theme =>
34 | val ret: StyleRules[P, String] = this.f(theme)
35 | val x: js.Function1[P, CSSProperties] = (props: P) => withThemeProps(theme, props)
36 | ret.update(key, x)
37 | ret
38 | })
39 |
40 | @inline def hook: StylesHook[Styles[T, P, String]] =
41 | makeStyles[Styles[T, P, String]](f)
42 |
43 | @inline def hook(opts: WithStylesOptions): StylesHook[Styles[T, P, String]] =
44 | makeStyles[Styles[T, P, String]](f, opts)
45 | end StyleBuilder
46 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/album/Album.scala:
--------------------------------------------------------------------------------
1 | package demo.album
2 |
3 | import demo.StyleBuilder
4 | import japgolly.scalajs.react.ScalaFnComponent
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import org.scalablytyped.runtime.StringDictionary
7 | import typings.classnames.mod as classNames
8 | import typings.csstype.csstypeStrings.{auto, column, flex, relative}
9 | import typings.materialUiCore.components.*
10 | import typings.materialUiCore.stylesCreateMuiThemeMod.Theme
11 | import typings.materialUiCore.materialUiCoreInts.*
12 | import typings.materialUiCore.materialUiCoreStrings as strings
13 | import typings.materialUiCore.mod.PropTypes.Color
14 | import typings.materialUiCore.typographyTypographyMod.Style
15 | import typings.materialUiIcons.components as Icons
16 | import typings.materialUiStyles.makeStylesMod.StylesHook
17 | import typings.materialUiStyles.withStylesMod.{CSSProperties, Styles}
18 | import typings.react.components.Fragment
19 |
20 | import scala.scalajs.js
21 |
22 | // https://v3.material-ui.com/getting-started/page-layout-examples/album/
23 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/getting-started/page-layout-examples/album/Album.js
24 | object Album:
25 | lazy val styles: StylesHook[Styles[Theme, js.Object, String]] =
26 | StyleBuilder[Theme, js.Object]
27 | .add("appBar", CSSProperties().setPosition(relative))
28 | .add("icon", theme => CSSProperties().setMarginRight(theme.spacing.unit * 2))
29 | .add("heroUnit", theme => CSSProperties().setBackgroundColor(theme.palette.background.paper))
30 | .add(
31 | "heroContent",
32 | theme =>
33 | CSSProperties()
34 | .setMaxWidth(600)
35 | .setMargin("0 auto")
36 | .setPadding(s"${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px")
37 | )
38 | .add("heroButtons", theme => CSSProperties().setMarginTop(theme.spacing.unit * 4))
39 | .add(
40 | "layout",
41 | theme =>
42 | CSSProperties()
43 | .setWidth("auto")
44 | .setMarginLeft(theme.spacing.unit * 3)
45 | .setMarginRight(theme.spacing.unit * 3)
46 | .set(
47 | theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2),
48 | CSSProperties()
49 | .setWidth(1100)
50 | .setMarginLeft(auto.asInstanceOf[String])
51 | .setMarginRight(auto.asInstanceOf[String])
52 | )
53 | )
54 | .add("cardGrid", theme => CSSProperties().setPadding(s"${theme.spacing.unit * 8}px 0"))
55 | .add("card", CSSProperties().setHeight("100%").setDisplay(flex).setFlexDirection(column))
56 | .add("cardMedia", CSSProperties().setPaddingTop("56.25%")) // 16:9
57 | .add("cardContent", CSSProperties().setFlexGrow(1))
58 | .add(
59 | "footer",
60 | theme => CSSProperties().setBackgroundColor(theme.palette.background.paper).setPadding(theme.spacing.unit * 6)
61 | )
62 | .hook
63 |
64 | val cards: Seq[Int] = 1 to 12
65 |
66 | type Props = Unit
67 |
68 | val component = ScalaFnComponent[Unit] { _ =>
69 | val classes = styles(js.undefined)
70 |
71 | Fragment(
72 | CssBaseline(),
73 | Stepper.connectorVdomElement(<.div("foo")),
74 | AppBar
75 | .position(strings.static)
76 | .className(classes("appBar"))(
77 | Toolbar(
78 | Icons.PhotoCamera.className(classes("icon"))(),
79 | Typography.variant(Style.h6).color(Color.inherit)("Album layout")
80 | )
81 | ),
82 | <.main(
83 | <.div(^.className := classes("heroUnit"))(
84 | <.div(^.className := classes("heroContent"))(
85 | Typography
86 | .variant(Style.h2)
87 | .align(strings.center)
88 | .color(strings.textSecondary)
89 | .gutterBottom(true)
90 | .component("h1")(
91 | "Album Layout"
92 | ),
93 | Typography
94 | .variant(Style.h6)
95 | .align(strings.center)
96 | .color(strings.textSecondary)
97 | .paragraph(true)(
98 | """Something short and leading about the collection below—its
99 | |contents, the creator, etc. Make it short and sweet, but not too
100 | |short so folks don't simply skip over it entirely.""".stripMargin
101 | ),
102 | <.div(^.className := classes("heroButtons"))(
103 | Grid
104 | .container(true)
105 | .spacing(`16`)
106 | .justify(strings.center)(
107 | Grid.item(true)(Button.variant(strings.contained).color(Color.primary)("Main call to action")),
108 | Grid.item(true)(Button.variant(strings.outlined).color(Color.primary)("Secondary action"))
109 | )
110 | )
111 | )
112 | ),
113 | <.div(
114 | ^.className := classNames(StringDictionary[js.Any](classes("layout") -> true, classes("cardGrid") -> true))
115 | )(
116 | Grid
117 | .container(true)
118 | .spacing(`40`)(cards.map { card =>
119 | Grid
120 | .item(true)
121 | .withKey(card.toString)
122 | .sm(`6`)
123 | .md(`4`)
124 | .lg(`3`)(
125 | Card.className(classes("card"))(
126 | CardMedia
127 | .className(classes("cardMedia"))
128 | .image(
129 | "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E"
130 | ) /* eslint-disable-line max-len*/
131 | .title("Image title"),
132 | CardContent
133 | .className("cardContent")(
134 | Typography
135 | .gutterBottom(true)
136 | .variant(Style.h5)
137 | .component("h2")("Heading"),
138 | Typography(
139 | "This is a media card. You can use this section to describe the content."
140 | )
141 | ),
142 | CardActions(
143 | Button.size(strings.small).color(Color.primary)("View"),
144 | Button.size(strings.small).color(Color.primary)("Edit")
145 | )
146 | )
147 | )
148 | .build
149 | }.toVdomArray)
150 | )
151 | ),
152 | <.footer(^.className := classes("footer"))(
153 | Typography
154 | .variant(Style.h6)
155 | .align(strings.center)
156 | .gutterBottom(true)(
157 | "Footer"
158 | ),
159 | Typography
160 | .variant(Style.subtitle1)
161 | .align(strings.center)
162 | .color(strings.textSecondary)
163 | .component("p")(
164 | "Something here to give the footer a purpose!"
165 | )
166 | )
167 | )
168 | }
169 | end Album
170 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/button/Button.scala:
--------------------------------------------------------------------------------
1 | package demo.button
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import org.scalablytyped.runtime.StringDictionary
6 | import org.scalajs.dom
7 | import typings.csstype.mod.{ColorProperty, NamedColor}
8 | import typings.materialUiCore.stylesCreateMuiThemeMod.{Theme, ThemeOptions}
9 | import typings.materialUiCore.stylesSpacingMod.SpacingOptions
10 | import typings.materialUiCore.{stylesMod, components as Mui}
11 | import typings.materialUiStyles.components.ThemeProvider
12 | import typings.react.mod.useState
13 | import typings.std.global.window
14 |
15 | import scala.scalajs.js
16 |
17 | object Button:
18 |
19 | val theme: Theme = stylesMod.createMuiTheme(ThemeOptions().setSpacing(SpacingOptions().setUnit(2)))
20 |
21 | // theme passed through react context and used in StyledButtonHooksDemo
22 | ThemeProvider(theme)(
23 | <.div(
24 | ButtonTest.component("dear user"),
25 | SelectDemo.component(List("one", "two", "three")),
26 | StyledButtonDemo.component(),
27 | StyledButtonHooksDemo.component()
28 | )
29 | ).renderIntoDOM(dom.document.getElementById("container"))
30 | end Button
31 |
32 | object ButtonTest:
33 |
34 | val component = ScalaFnComponent[String] { name =>
35 | /* use a hook to keep state */
36 | val js.Tuple2(state, setState) = useState(1)
37 |
38 | val incrementButton = Mui.Button.onClick(_ => Callback(setState(state + 1)))(
39 | s"Increment it, $name"
40 | )
41 |
42 | <.div(
43 | /* text field controlled by the value of the state hook above*/
44 | Mui.TextField.StandardTextFieldProps().value(state).disabled(true),
45 | incrementButton
46 | )
47 | }
48 | end ButtonTest
49 |
50 | object SelectDemo:
51 |
52 | val component = ScalaFnComponent[List[String]] { case values =>
53 | val js.Tuple2(chosen, setChosen) = useState[String](values.head)
54 |
55 | val items = values.zipWithIndex.map { case (value, idx) =>
56 | Mui.MenuItem.value(value).withKey(idx.toString)(value).build
57 | }.toVdomArray
58 |
59 | <.div(
60 | Mui.Select
61 | .value(chosen)
62 | .onChange((e, _) => Callback(setChosen(e.target.value)))(items),
63 | Mui.TextField
64 | .StandardTextFieldProps()
65 | .value(chosen)
66 | .disabled(true)
67 | )
68 | }
69 | end SelectDemo
70 |
71 | object StyledButtonDemo:
72 |
73 | val component = ScalaFnComponent[Unit] { _ =>
74 | val usingWithStyles =
75 | import typings.materialUiCore.stylesWithStylesMod.{CSSProperties, WithStylesOptions}
76 |
77 | val styleInjector =
78 | stylesMod.withStyles(
79 | StringDictionary("root" -> CSSProperties().setBackgroundColor(NamedColor.blue)),
80 | WithStylesOptions[String]()
81 | )
82 |
83 | Mui.Button
84 | .withComponent(c => styleInjector(c).asInstanceOf[js.Any])
85 | .onClick(_ => Callback(window.alert("clicked")))("using withStyles")
86 | end usingWithStyles
87 |
88 | val usingReactCss =
89 | import typings.react.mod.CSSProperties
90 | Mui.Button
91 | .style(CSSProperties().setBackgroundColor(NamedColor.darkred))
92 | .onClick(_ => Callback(window.alert("clicked")))("direct css")
93 |
94 | <.div(usingWithStyles, usingReactCss)
95 | }
96 | end StyledButtonDemo
97 |
98 | // https://v3.material-ui.com/css-in-js/basics/
99 | object StyledButtonHooksDemo:
100 |
101 | import typings.materialUiStyles.makeStylesMod.StylesHook
102 | import typings.materialUiStyles.mod.makeStyles
103 | import typings.materialUiStyles.withStylesMod.{CSSProperties, StyleRulesCallback, Styles, WithStylesOptions}
104 |
105 | class StyleProps(val favouriteColor: ColorProperty) extends js.Object
106 |
107 | val useStyles: StylesHook[Styles[Theme, StyleProps, String]] =
108 | val root: js.Function1[StyleProps, CSSProperties] = props =>
109 | CSSProperties()
110 | .setBackground("linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)")
111 | .setBorder(0)
112 | .setBorderRadius(3)
113 | .setBoxShadow("0 3px 5px 2px rgba(255, 105, 135, .3)")
114 | .setColor(props.favouriteColor)
115 | .setHeight(48)
116 | .setPadding("0 30px")
117 |
118 | /* If you don't need direct access to theme, this could be `StyleRules[Props, String]` */
119 | val stylesCallback: StyleRulesCallback[Theme, StyleProps, String] = theme =>
120 | StringDictionary(
121 | "root" -> root,
122 | "outer" -> CSSProperties().setPadding(s"${theme.spacing.unit * 3}px")
123 | )
124 |
125 | makeStyles(stylesCallback, WithStylesOptions())
126 | end useStyles
127 |
128 | val component = ScalaFnComponent[Unit] { _ =>
129 | val classes = useStyles(new StyleProps(NamedColor.green))
130 | <.div(
131 | ^.className := classes("outer"),
132 | Mui.Button
133 | .className(classes("root"))
134 | .onClick(_ => Callback.alert("clicked"))("styles module with hook")
135 | )
136 | }
137 | end StyledButtonHooksDemo
138 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/components/AppTheme.scala:
--------------------------------------------------------------------------------
1 | package demo.components
2 |
3 | import demo.StyleBuilder
4 | import japgolly.scalajs.react.ScalaFnComponent
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import org.scalablytyped.runtime.StringDictionary
7 | import typings.classnames.mod as classNames
8 | import typings.csstype.csstypeStrings.absolute
9 | import typings.materialUiCore.stylesColorManipulatorMod.darken
10 | import typings.materialUiCore.colorsMod.{blue, pink}
11 | import typings.materialUiCore.components.*
12 | import typings.materialUiCore.stylesCreateMuiThemeMod.{Direction, Theme, ThemeOptions}
13 | import typings.materialUiCore.stylesCreatePaletteMod.{ColorPartial, PaletteColorOptions, PaletteOptions}
14 | import typings.materialUiCore.stylesCreateTypographyMod.TypographyOptions
15 | import typings.materialUiCore.materialUiCoreStrings.{center, textSecondary}
16 | import typings.materialUiCore.mod.PropTypes.Color
17 | import typings.materialUiCore.stylesMod.createMuiTheme
18 | import typings.materialUiStyles.makeStylesMod.StylesHook
19 | import typings.materialUiStyles.withStylesMod.{CSSProperties, Styles}
20 |
21 | import scala.scalajs.js
22 |
23 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/modules/components/AppTheme.js
24 | object AppTheme:
25 |
26 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/modules/styles/themeInitialState.js
27 | val theme: Theme = createMuiTheme(
28 | ThemeOptions()
29 | .setDirection(Direction.ltr)
30 | .setTypography(TypographyOptions().setUseNextVariants(true))
31 | .setPalette(
32 | PaletteOptions()
33 | .setPrimary(ColorPartial().combineWith(blue))
34 | .setSecondary(
35 | PaletteColorOptions.SimplePaletteColorOptions(darken(pink.A400, 0.08))
36 | ) // Darken so we reach the AA contrast ratio level.
37 | )
38 | )
39 |
40 | lazy val styles: StylesHook[Styles[Theme, js.Object, String]] =
41 | StyleBuilder[Theme, js.Object]
42 | .add(
43 | "credit",
44 | theme => CSSProperties().setMarginTop(theme.spacing.unit * 6).setMarginBottom(theme.spacing.unit * 4)
45 | )
46 | .add("hideCredit", CSSProperties().setPosition(absolute).set("top", 0))
47 | .hook
48 |
49 | case class Props(description: String, hideCredit: Boolean = false, title: String)
50 |
51 | val component = ScalaFnComponent.withChildren[Props] { case (props, children) =>
52 | val classes = styles(js.undefined)
53 | MuiThemeProvider(theme)(
54 | children,
55 | Typography
56 | .color(textSecondary)
57 | .align(center)
58 | .className(
59 | classNames(StringDictionary[js.Any](classes("credit") -> true, classes("hideCredit") -> props.hideCredit))
60 | )(
61 | "Built with ",
62 | <.span(^.role := "img", ^.aria.label := "Love")("❤️"),
63 | " by the ",
64 | Link.color(Color.inherit).href("www.scalablytyped.org")("ScalablyTyped Material-UI"),
65 | " team."
66 | )
67 | )
68 | }
69 | end AppTheme
70 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/customization/DarkTheme.scala:
--------------------------------------------------------------------------------
1 | package demo.customization
2 |
3 | import typings.materialUiCore.stylesCreateMuiThemeMod.{Theme, ThemeOptions}
4 | import typings.materialUiCore.stylesCreatePaletteMod.PaletteOptions
5 | import typings.materialUiCore.stylesCreateTypographyMod.TypographyOptions
6 | import typings.materialUiCore.mod.PaletteType
7 | import typings.materialUiCore.stylesMod.createMuiTheme
8 | import typings.materialUiStyles.components.ThemeProvider
9 |
10 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/customization/themes/DarkTheme.js
11 | object DarkTheme:
12 | val theme: Theme = createMuiTheme(
13 | ThemeOptions()
14 | .setTypography(TypographyOptions().setUseNextVariants(true))
15 | .setPalette(
16 | PaletteOptions().setType(PaletteType.dark) /* Switching the dark mode on is a single property value change.*/
17 | )
18 | )
19 |
20 | def apply() =
21 | ThemeProvider(theme)(WithTheme.component(theme))
22 | end DarkTheme
23 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/customization/Palette.scala:
--------------------------------------------------------------------------------
1 | package demo.customization
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import typings.materialUiCore.colorsMod.purple
6 | import typings.materialUiCore.components.{Button, MuiThemeProvider}
7 | import typings.materialUiCore.stylesCreateMuiThemeMod.{Theme, ThemeOptions}
8 | import typings.materialUiCore.stylesCreatePaletteMod.{PaletteColorOptions, PaletteOptions}
9 | import typings.materialUiCore.stylesCreateTypographyMod.TypographyOptions
10 | import typings.materialUiCore.mod.PropTypes.Color
11 | import typings.materialUiCore.stylesMod
12 |
13 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/customization/themes/Palette.js
14 | object Palette:
15 |
16 | val theme: Theme = stylesMod
17 | .createMuiTheme(
18 | ThemeOptions()
19 | .setTypography(TypographyOptions().setUseNextVariants(true))
20 | .setPalette(
21 | PaletteOptions()
22 | .setPrimary(
23 | PaletteColorOptions.SimplePaletteColorOptions(purple.`500`)
24 | ) // Purple and green play nicely together.
25 | .setSecondary(PaletteColorOptions.SimplePaletteColorOptions("#11cb5f")) // This is just green.A700 as hex.
26 | )
27 | )
28 |
29 | val component = ScalaFnComponent[Unit] { _ =>
30 | MuiThemeProvider(theme)(
31 | Button.color(Color.primary)(<.span("Primary")),
32 | Button.color(Color.secondary)(<.span("Secondary"))
33 | )
34 | }
35 | end Palette
36 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/customization/WithTheme.scala:
--------------------------------------------------------------------------------
1 | package demo.customization
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import typings.materialUiCore.components.Typography
6 | import typings.materialUiCore.stylesCreateMuiThemeMod.Theme
7 | import typings.react.mod.CSSProperties
8 |
9 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/customization/themes/WithTheme.js
10 | object WithTheme:
11 | val component = ScalaFnComponent[Theme] { theme =>
12 | val primaryText = theme.palette.text.primary;
13 | val primaryColor = theme.palette.primary.main;
14 |
15 | <.div(^.style := CSSProperties().setWidth(300))(
16 | Typography.style(
17 | CSSProperties()
18 | .setBackgroundColor(primaryColor)
19 | .setPadding(s"${theme.spacing.unit}px ${theme.spacing.unit * 2}px")
20 | .setColor(theme.palette.common.white)
21 | )(s"Primary color $primaryColor"),
22 | Typography.style(
23 | CSSProperties()
24 | .setBackgroundColor(theme.palette.background.default)
25 | .setPadding(s"${theme.spacing.unit}px ${theme.spacing.unit * 2}px")
26 | .setColor(primaryText)
27 | )(s"Primary color $primaryText")
28 | )
29 | }
30 | end WithTheme
31 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/dashboard/Dashboard.scala:
--------------------------------------------------------------------------------
1 | package demo.dashboard
2 |
3 | import demo.StyleBuilder
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
6 | import org.scalablytyped.runtime.StringDictionary
7 | import typings.classnames.mod as classNames
8 | import typings.csstype.csstypeStrings.*
9 | import typings.csstype.mod.OverflowXProperty
10 | import typings.materialUiCore.anon.{PartialClassNameMapDrawer, Partialdurationnumberstri}
11 | import typings.materialUiCore.components.*
12 | import typings.materialUiCore.stylesCreateBreakpointsMod.Breakpoint
13 | import typings.materialUiCore.stylesCreateMuiThemeMod.Theme
14 | import typings.materialUiCore.materialUiCoreStrings.{absolute, permanent}
15 | import typings.materialUiCore.mod.PropTypes
16 | import typings.materialUiCore.typographyTypographyMod.Style
17 | import typings.materialUiIcons.components as Icons
18 | import typings.materialUiStyles.makeStylesMod.StylesHook
19 | import typings.materialUiStyles.withStylesMod.{CSSProperties, Styles}
20 | import typings.react.mod.useState
21 |
22 | import scala.scalajs.js
23 |
24 | // https://v3.material-ui.com/getting-started/page-layout-examples/dashboard/
25 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/getting-started/page-layout-examples/dashboard/Dashboard.js
26 | object Dashboard:
27 |
28 | val drawerWidth = 240
29 |
30 | lazy val styles: StylesHook[Styles[Theme, js.Object, String]] =
31 | StyleBuilder[Theme, js.Object]
32 | .add("root", CSSProperties().setDisplay(flex))
33 | .add("toolbar", CSSProperties().setPaddingRight(24)) // keep right padding when drawer closed
34 | .add(
35 | "toolbarIcon",
36 | theme =>
37 | CSSProperties()
38 | .setDisplay(flex)
39 | .setAlignItems(center)
40 | .setJustifyContent(`flex-end`)
41 | .setPadding("0 8px")
42 | .combineWith(theme.mixins.toolbar)
43 | )
44 | .add(
45 | "appBar",
46 | theme =>
47 | CSSProperties()
48 | .setZIndex(theme.zIndex.drawer + 1)
49 | .setTransition(
50 | theme.transitions.create(
51 | js.Array("width", "margin"),
52 | Partialdurationnumberstri()
53 | .setEasing(theme.transitions.easing.sharp)
54 | .setDuration(theme.transitions.duration.enteringScreen)
55 | )
56 | )
57 | )
58 | .add(
59 | "appBarShift",
60 | theme =>
61 | CSSProperties()
62 | .setMarginLeft(drawerWidth)
63 | .setWidth(s"calc(100% - ${drawerWidth}px)")
64 | .setTransition(
65 | theme.transitions.create(
66 | js.Array("width", "margin"),
67 | Partialdurationnumberstri()
68 | .setEasing(theme.transitions.easing.sharp)
69 | .setDuration(theme.transitions.duration.enteringScreen)
70 | )
71 | )
72 | )
73 | .add("menuButton", CSSProperties().setMarginLeft(12).setMarginRight(36))
74 | .add("menuButtonHidden", CSSProperties().setDisplay(none))
75 | .add("title", CSSProperties().setFlexGrow(1))
76 | .add(
77 | "drawerPaper",
78 | theme =>
79 | CSSProperties()
80 | .setPosition(relative)
81 | .setWhiteSpace(nowrap)
82 | .setWidth(drawerWidth)
83 | .setTransition(
84 | theme.transitions.create(
85 | "width",
86 | Partialdurationnumberstri()
87 | .setEasing(theme.transitions.easing.sharp)
88 | .setDuration(theme.transitions.duration.enteringScreen)
89 | )
90 | )
91 | )
92 | .add(
93 | "drawerPaperClose",
94 | theme =>
95 | CSSProperties()
96 | .setOverflowX(OverflowXProperty.hidden)
97 | .setTransition(
98 | theme.transitions.create(
99 | "width",
100 | Partialdurationnumberstri()
101 | .setEasing(theme.transitions.easing.sharp)
102 | .setDuration(theme.transitions.duration.leavingScreen)
103 | )
104 | )
105 | .setWidth(theme.spacing.unit * 7)
106 | .set(theme.breakpoints.up(Breakpoint.sm), CSSProperties().setWidth(theme.spacing.unit * 9))
107 | )
108 | .add("appBarSpacer", theme => CSSProperties().combineWith(theme.mixins.toolbar))
109 | .add(
110 | "content",
111 | theme =>
112 | CSSProperties()
113 | .setFlexGrow(1)
114 | .setPadding(theme.spacing.unit * 3)
115 | .setHeight("100vh")
116 | .setOverflow(auto)
117 | )
118 | .add("chartContainer", CSSProperties().setMarginLeft(-22))
119 | .add("tableContainer", CSSProperties().setHeight(320))
120 | .add("h5", theme => CSSProperties().setMarginBottom(theme.spacing.unit * 2))
121 | .hook
122 |
123 | val component = ScalaFnComponent[Unit] { case () =>
124 | val classes = styles(js.undefined)
125 | val js.Tuple2(isOpen, setIsOpen) = useState(true)
126 | val handleDrawerOpen = Callback(setIsOpen(true))
127 | val handleDrawerClose = Callback(setIsOpen(false))
128 |
129 | <.div(
130 | ^.className := classes("root"),
131 | CssBaseline(),
132 | AppBar
133 | .position(absolute)
134 | .className(classNames(StringDictionary[js.Any](classes("appBar") -> true, classes("appBarShift") -> isOpen)))(
135 | Toolbar
136 | .disableGutters(!isOpen)
137 | .className(classes("toolbar"))(
138 | IconButton
139 | .color(PropTypes.Color.inherit)
140 | .`aria-label`("Open drawer")
141 | .onClick(_ => handleDrawerOpen)
142 | .className(
143 | classNames(
144 | StringDictionary[js.Any](classes("menuButton") -> true, classes("menuButtonHidden") -> isOpen)
145 | )
146 | )(Icons.Menu()),
147 | Typography()
148 | .component("h1")
149 | .variant(Style.h6)
150 | .color(PropTypes.Color.inherit)
151 | .noWrap(true)
152 | .className(classes("title"))(
153 | "Dashboard"
154 | ),
155 | IconButton.color(PropTypes.Color.inherit)(
156 | Badge.badgeContent(4).color(PropTypes.Color.secondary)(Icons.Notifications())
157 | )
158 | )
159 | ),
160 | Drawer
161 | .variant(permanent)
162 | .classes(
163 | PartialClassNameMapDrawer().setPaper(
164 | classNames(
165 | StringDictionary[js.Any](classes("drawerPaper") -> true, classes("drawerPaperClose") -> !isOpen)
166 | )
167 | )
168 | )
169 | .open(isOpen)(
170 | <.div(^.className := classes("toolbarIcon"))(
171 | IconButton.onClick(_ => handleDrawerClose)(Icons.ChevronLeft())
172 | ),
173 | Divider(),
174 | List(ListItems.mainListItems),
175 | Divider(),
176 | List(ListItems.secondaryListItems)
177 | ),
178 | <.main(^.className := classes("content"))(
179 | <.div(^.className := classes("appBarSpacer")),
180 | Typography
181 | .variant(Style.h4)
182 | .gutterBottom(true)
183 | .component("h2")(
184 | "Orders"
185 | ),
186 | Typography()
187 | .component("div")
188 | .className(classes("chartContainer"))(
189 | SimpleLineChart.component()
190 | ),
191 | Typography()
192 | .variant(Style.h4)
193 | .gutterBottom(true)
194 | .component("h2")(
195 | "Products"
196 | ),
197 | Typography()
198 | .component("div")
199 | .className(classes("tableContainer"))(SimpleTable.component())
200 | )
201 | )
202 | }
203 | end Dashboard
204 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/dashboard/ListItems.scala:
--------------------------------------------------------------------------------
1 | package demo.dashboard
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import typings.materialUiCore.components.{ListItem, ListItemIcon, ListItemText, ListSubheader}
5 | import typings.materialUiIcons.components as Icon
6 |
7 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/getting-started/page-layout-examples/dashboard/listItems.js
8 | object ListItems:
9 |
10 | val mainListItems: VdomElement =
11 | <.div(
12 | ListItem.button(true)(
13 | ListItemIcon(Icon.Dashboard()),
14 | ListItemText.primary("Dashboard")
15 | ),
16 | ListItem.button(true)(
17 | ListItemIcon(Icon.ShoppingCart()),
18 | ListItemText.primary("Orders")
19 | ),
20 | ListItem.button(true)(
21 | ListItemIcon(Icon.People()),
22 | ListItemText.primary("Customers")
23 | ),
24 | ListItem.button(true)(
25 | ListItemIcon(Icon.BarChart()),
26 | ListItemText.primary("Reports")
27 | ),
28 | ListItem.button(true)(
29 | ListItemIcon(Icon.Layers()),
30 | ListItemText.primary("Integrations")
31 | )
32 | )
33 |
34 | val secondaryListItems: VdomElement =
35 | <.div(
36 | ListSubheader.inset(true)("Saved reports"),
37 | ListItem.button(true)(
38 | ListItemIcon(Icon.Assignment()),
39 | ListItemText.primary("Current month")
40 | ),
41 | ListItem.button(true)(
42 | ListItemIcon(Icon.Assignment()),
43 | ListItemText.primary("Last quarter")
44 | ),
45 | ListItem.button(true)(
46 | ListItemIcon(Icon.Assignment()),
47 | ListItemText.primary("Year-end sale")
48 | )
49 | )
50 | end ListItems
51 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/dashboard/SimpleLineChart.scala:
--------------------------------------------------------------------------------
1 | package demo.dashboard
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import org.scalablytyped.runtime.StringDictionary
6 | import typings.recharts.components.*
7 | import typings.recharts.rechartsStrings.monotone
8 |
9 | import scala.scalajs.js
10 |
11 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/getting-started/page-layout-examples/dashboard/SimpleLineChart.js
12 | object SimpleLineChart:
13 |
14 | class Data(val Name: String, val Visits: Int, val Orders: Int) extends js.Object
15 |
16 | val data: js.Array[js.Object] = js.Array(
17 | new Data("Mon", 200, 3400),
18 | new Data("Tue", 128, 2398),
19 | new Data("Wed", 5000, 4300),
20 | new Data("Thu", 4780, 2908),
21 | new Data("Fri", 5890, 4800),
22 | new Data("Sat", 4390, 3800),
23 | new Data("Sun", 4490, 4300)
24 | )
25 |
26 | val component = ScalaFnComponent[Unit] { case () =>
27 | ResponsiveContainer
28 | .width("99%")
29 | .height(320)(
30 | LineChart.data(data)(
31 | XAxis.dataKey("Name"),
32 | YAxis(),
33 | CartesianGrid.vertical(false).strokeDasharray("3 3"),
34 | Tooltip(),
35 | Legend(),
36 | Line("Visits").`type`(monotone).stroke("#82ca9d"),
37 | Line("Orders").`type`(monotone).stroke("#8884d8").activeDot(StringDictionary("r" -> 8))
38 | )
39 | )
40 | }
41 | end SimpleLineChart
42 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/dashboard/SimpleTable.scala:
--------------------------------------------------------------------------------
1 | package demo.dashboard
2 |
3 | import demo.StyleBuilder
4 | import japgolly.scalajs.react.ScalaFnComponent
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import typings.csstype.csstypeStrings.auto
7 | import typings.materialUiCore.components.*
8 | import typings.materialUiCore.stylesCreateMuiThemeMod.Theme
9 | import typings.materialUiCore.materialUiCoreStrings.right
10 | import typings.materialUiStyles.withStylesMod.CSSProperties
11 |
12 | import scala.scalajs.js
13 |
14 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/getting-started/page-layout-examples/dashboard/SimpleTable.js
15 | object SimpleTable:
16 |
17 | lazy val styles =
18 | StyleBuilder[Theme, js.Object]
19 | .add("root", CSSProperties().setWidth("100%").setOverflowX(auto))
20 | .add("table", CSSProperties().setMinWidth(700))
21 | .hook
22 |
23 | case class Data(id: Long, name: String, calories: Double, fat: Double, carbs: Double, protein: Double)
24 |
25 | val data = Seq(
26 | Data(1, "Frozen yoghurt", 159, 6.0, 24, 4.0),
27 | Data(2, "Ice cream sandwich", 237, 9.0, 37, 4.3),
28 | Data(3, "Eclair", 262, 16.0, 24, 6.0),
29 | Data(4, "Cupcake", 305, 3.7, 67, 4.3),
30 | Data(5, "Gingerbread", 356, 16.0, 49, 3.9)
31 | )
32 |
33 | val component = ScalaFnComponent[Unit] { case () =>
34 | val classes = styles(js.undefined)
35 | Paper.className(classes("root"))(
36 | Table.className(classes("table"))(
37 | TableHead(
38 | TableRow(
39 | TableCell("Dessert (100g serving)"),
40 | TableCell.align(right)("Calories"),
41 | TableCell.align(right)("Fat (g)"),
42 | TableCell.align(right)("Carbs (g)"),
43 | TableCell.align(right)("Protein (g)")
44 | )
45 | ),
46 | TableBody(data.map { n =>
47 | TableRow
48 | .withKey(n.id.toString)(
49 | TableCell.set("component", "th").scope("row")(n.name),
50 | TableCell.align(right)(n.calories),
51 | TableCell.align(right)(n.fat),
52 | TableCell.align(right)(n.carbs),
53 | TableCell.align(right)(n.protein)
54 | )
55 | .build
56 | }.toVdomArray)
57 | )
58 | )
59 | }
60 | end SimpleTable
61 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/login/Login.scala:
--------------------------------------------------------------------------------
1 | package demo.login
2 |
3 | import demo.login.Styles.styles
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
6 | import org.scalablytyped.runtime.StringDictionary
7 | import typings.classnames.mod as classNames
8 | import typings.materialUiCore.anon.{PartialClassNameMapInputC, PartialInputProps}
9 | import typings.materialUiCore.components.*
10 | import typings.materialUiCore.materialUiCoreStrings.*
11 | import typings.materialUiCore.typographyTypographyMod.Style
12 | import typings.react.components.Fragment
13 | import typings.react.mod.useState
14 | import typings.std.HTMLInputElement
15 |
16 | import scala.scalajs.js
17 | import scala.scalajs.js.annotation.JSImport
18 |
19 | @JSImport("./logo.svg", JSImport.Default)
20 | @js.native
21 | object Logo extends js.Object
22 |
23 | @JSImport("./google.svg", JSImport.Default)
24 | @js.native
25 | object GoogleLogo extends js.Object
26 |
27 | // https://github.com/flatlogic/react-material-admin/blob/master/src/pages/login/Login.js
28 | object Login:
29 |
30 | val component = ScalaFnComponent[Unit] { _ =>
31 | val classes = styles(js.undefined)
32 |
33 | val js.Tuple2(isLoading, setIsLoading) = useState(false);
34 | val js.Tuple2(error, setError) = useState(false);
35 | val js.Tuple2(activeTabId, setActiveTabId) = useState(0);
36 | val js.Tuple2(nameValue, setNameValue) = useState("");
37 | val js.Tuple2(loginValue, setLoginValue) = useState("");
38 | val js.Tuple2(passwordValue, setPasswordValue) = useState("");
39 |
40 | Grid
41 | .container(true)
42 | .className(classes("container"))(
43 | <.div(^.className := classes("logotypeContainer"))(
44 | <.img(^.src := Logo.asInstanceOf[String], ^.alt := "logo", ^.className := classes("logotypeImage")),
45 | Typography.className(classes("logotypeText"))("Material Admin")
46 | ),
47 | <.div(^.className := classes("formContainer"))(
48 | <.div(^.className := classes("form"))(
49 | Tabs(activeTabId)
50 | .onChange((_, id) => Callback(setActiveTabId(id.asInstanceOf[Int])))
51 | .indicatorColor(primary)
52 | .textColor(primary)
53 | .centered(true)(
54 | Tab.label("Login").className(classNames(StringDictionary[js.Any]("root" -> classes("tab")))),
55 | Tab.label("New User").className(classNames(StringDictionary[js.Any]("root" -> classes("tab"))))
56 | ),
57 | activeTabId match
58 | case 0 =>
59 | Fragment(
60 | Typography
61 | .variant(Style.h1)
62 | .className(classes("greeting"))("Good Morning, User"),
63 | Button
64 | .size(large)
65 | .className(classes("googleButton"))(
66 | <.img(
67 | ^.src := GoogleLogo.asInstanceOf[String],
68 | ^.alt := "google",
69 | ^.className := classes("googleIcon")
70 | ),
71 | "Sign in with Google"
72 | ),
73 | <.div(^.className := classes("formDividerContainer"))(
74 | <.div(^.className := classes("formDivider")),
75 | Typography.className(classes("formDividerWord"))("or"),
76 | <.div(^.className := classes("formDivider"))
77 | ),
78 | Fade.in(error)(
79 | Typography
80 | .color(secondary)
81 | .className(classes("errorMessage"))("Something is wrong with your login or password :(")
82 | ),
83 | TextField.StandardTextFieldProps
84 | .id("email")
85 | .InputProps(
86 | PartialInputProps()
87 | .setClasses(
88 | PartialClassNameMapInputC()
89 | .setUnderline(classes("textFieldUnderline"))
90 | .setInput(classes("textField"))
91 | )
92 | )
93 | .value(loginValue)
94 | .onChange(e => Callback(setLoginValue(e.currentTarget.asInstanceOf[HTMLInputElement].value)))
95 | .margin(normal)
96 | .placeholder("Email Address")
97 | .`type`("email")
98 | .fullWidth(true),
99 | TextField.StandardTextFieldProps
100 | .id("password")
101 | .InputProps(
102 | PartialInputProps()
103 | .setClasses(
104 | PartialClassNameMapInputC()
105 | .setUnderline(classes("textFieldUnderline"))
106 | .setInput(classes("textField"))
107 | )
108 | )
109 | .value(passwordValue)
110 | .onChange(e => Callback(setPasswordValue(e.currentTarget.asInstanceOf[HTMLInputElement].value)))
111 | .margin(normal)
112 | .placeholder("Password")
113 | .`type`("password")
114 | .fullWidth(true),
115 | <.div(^.className := classes("formButtons"))(
116 | if isLoading then CircularProgress.size(26).className(classes("loginLoader"))
117 | else
118 | Button
119 | .disabled(loginValue.length == 0 || passwordValue.length == 0)
120 | .variant(contained)
121 | .color(primary)
122 | .size(large)("Login")
123 | ,
124 | Button
125 | .color(primary)
126 | .size(large)
127 | .className(classes("forgetButton"))("Forget Password")
128 | )
129 | )
130 | case 1 =>
131 | Fragment(
132 | Typography
133 | .variant(Style.h1)
134 | .className(classes("greeting"))("Welcome"),
135 | Typography.variant(Style.h2).className(classes("subGreeting"))("Create your account"),
136 | Fade.in(error)(
137 | Typography
138 | .color(secondary)
139 | .className(classes("errorMessage"))("Something is wrong with your login or password :(")
140 | ),
141 | TextField.StandardTextFieldProps
142 | .id("name")
143 | .InputProps(
144 | PartialInputProps()
145 | .setClasses(
146 | PartialClassNameMapInputC()
147 | .setUnderline(classes("textFieldUnderline"))
148 | .setInput(classes("textField"))
149 | )
150 | )
151 | .value(nameValue)
152 | .onChange(e => Callback(setNameValue(e.currentTarget.asInstanceOf[HTMLInputElement].value)))
153 | .margin(normal)
154 | .placeholder("Full Name")
155 | .`type`("text")
156 | .fullWidth(true),
157 | TextField.StandardTextFieldProps
158 | .id("email")
159 | .InputProps(
160 | PartialInputProps()
161 | .setClasses(
162 | PartialClassNameMapInputC()
163 | .setUnderline(classes("textFieldUnderline"))
164 | .setInput(classes("textField"))
165 | )
166 | )
167 | .value(loginValue)
168 | .onChange(e => Callback(setLoginValue(e.currentTarget.asInstanceOf[HTMLInputElement].value)))
169 | .margin(normal)
170 | .placeholder("Email Address")
171 | .`type`("email")
172 | .fullWidth(true),
173 | TextField.StandardTextFieldProps
174 | .id("password")
175 | .InputProps(
176 | PartialInputProps()
177 | .setClasses(
178 | PartialClassNameMapInputC()
179 | .setUnderline(classes("textFieldUnderline"))
180 | .setInput(classes("textField"))
181 | )
182 | )
183 | .value(passwordValue)
184 | .onChange(e => Callback(setPasswordValue(e.currentTarget.asInstanceOf[HTMLInputElement].value)))
185 | .margin(normal)
186 | .placeholder("Password")
187 | .`type`("password")
188 | .fullWidth(true),
189 | <.div(^.className := classes("formButtons"))(
190 | if isLoading then CircularProgress.size(26).className(classes("loginLoader"))
191 | else
192 | Button
193 | .disabled(loginValue.length == 0 || passwordValue.length == 0)
194 | .size(large)
195 | .variant(contained)
196 | .color(primary)
197 | .fullWidth(true)
198 | .className(classes("createAccountButton"))("Create your account")
199 | ),
200 | <.div(^.className := classes("formDividerContainer"))(
201 | <.div(^.className := classes("formDivider")),
202 | Typography.className(classes("formDividerWord"))("or"),
203 | <.div(^.className := classes("formDivider"))
204 | ),
205 | Button
206 | .color(primary)
207 | .size(large)
208 | .className(
209 | classNames(
210 | StringDictionary[js.Any](
211 | classes("googleButton") -> true,
212 | classes("googleButtonCreating") -> true
213 | )
214 | )
215 | )(
216 | <.img(
217 | ^.src := GoogleLogo.asInstanceOf[String],
218 | ^.alt := "google",
219 | ^.className := classes("googleIcon")
220 | ),
221 | "Sign in with Google"
222 | )
223 | )
224 | ),
225 | Typography
226 | .color(primary)
227 | .className(classes("copyright"))("© 2014-2019 Flatlogic, LLC. All rights reserved.")
228 | )
229 | )
230 | }
231 | end Login
232 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/login/Styles.scala:
--------------------------------------------------------------------------------
1 | package demo.login
2 |
3 | import demo.StyleBuilder
4 | import typings.csstype.csstypeStrings.*
5 | import typings.materialUiCore.stylesCreateMuiThemeMod.Theme
6 | import typings.materialUiCore.stylesSpacingMod.Spacing
7 | import typings.materialUiStyles.makeStylesMod.StylesHook
8 | import typings.materialUiStyles.withStylesMod.{CSSProperties, Styles}
9 |
10 | import scala.scalajs.js
11 |
12 | // https://github.com/flatlogic/react-material-admin/blob/master/src/pages/login/styles.js
13 | object Styles:
14 |
15 | lazy val styles: StylesHook[Styles[Theme, js.Object, String]] =
16 | StyleBuilder[Theme, js.Object]
17 | .add(
18 | "container",
19 | theme =>
20 | CSSProperties()
21 | .setHeight("100vh")
22 | .setWidth("100vw")
23 | .setDisplay(flex)
24 | .setJustifyContent(center)
25 | .setAlignItems(center)
26 | .setPosition(typings.csstype.csstypeStrings.absolute)
27 | .setTop(0)
28 | .setLeft(0)
29 | )
30 | .add(
31 | "logotypeContainer",
32 | theme =>
33 | CSSProperties()
34 | .setBackgroundColor(theme.palette.primary.main)
35 | .setWidth("60%")
36 | .setHeight("100%")
37 | .setDisplay(flex)
38 | .setFlexDirection(typings.csstype.csstypeStrings.column)
39 | .setJustifyContent(center)
40 | .setAlignItems(center)
41 | .set(
42 | theme.breakpoints.down(typings.materialUiCore.materialUiCoreStrings.md),
43 | CSSProperties()
44 | .setWidth("50%")
45 | )
46 | .set(
47 | theme.breakpoints.down(typings.materialUiCore.materialUiCoreStrings.md),
48 | CSSProperties()
49 | .setDisplay(none)
50 | )
51 | )
52 | .add(
53 | "logotypeImage",
54 | theme =>
55 | CSSProperties()
56 | .setWidth(165)
57 | .setMarginBottom(theme.setSpacing(Spacing(4)).spacing.unit)
58 | )
59 | .add(
60 | "logotypeText",
61 | theme =>
62 | CSSProperties()
63 | .setColor("white")
64 | .setFontWeight(500)
65 | .setFontSize(84)
66 | .set(
67 | theme.breakpoints.down(typings.materialUiCore.materialUiCoreStrings.md),
68 | CSSProperties()
69 | .setFontSize(48)
70 | )
71 | )
72 | .add(
73 | "formContainer",
74 | theme =>
75 | CSSProperties()
76 | .setWidth("40%")
77 | .setHeight("100%")
78 | .setDisplay(flex)
79 | .setFlexDirection(column)
80 | .setJustifyContent(center)
81 | .setAlignItems(center)
82 | .set(
83 | theme.breakpoints.down(typings.materialUiCore.materialUiCoreStrings.md),
84 | CSSProperties()
85 | .setWidth("50%")
86 | )
87 | )
88 | .add(
89 | "form",
90 | theme =>
91 | CSSProperties()
92 | .setWidth(320)
93 | )
94 | .add(
95 | "tab",
96 | theme =>
97 | CSSProperties()
98 | .setFontWeight(400)
99 | .setFontSize(18)
100 | )
101 | .add(
102 | "greeting",
103 | theme =>
104 | CSSProperties()
105 | .setFontWeight(500)
106 | .setTextAlign(center)
107 | .setMarginTop(theme.setSpacing(Spacing(4)).spacing.unit)
108 | )
109 | .add(
110 | "subGreeting",
111 | theme =>
112 | CSSProperties()
113 | .setFontWeight(500)
114 | .setTextAlign(center)
115 | .setMarginTop(theme.setSpacing(Spacing(2)).spacing.unit)
116 | )
117 | .add(
118 | "googleButton",
119 | theme =>
120 | CSSProperties()
121 | .setMarginTop(theme.setSpacing(Spacing(6)).spacing.unit)
122 | //.setBoxShadow( theme.customShadows.widget)
123 | .setBackgroundColor("white")
124 | .setWidth("100%")
125 | .setTextTransform(none)
126 | )
127 | .add(
128 | "googleButtonCreating",
129 | theme =>
130 | CSSProperties()
131 | .setMarginTop(0)
132 | )
133 | .add(
134 | "googleIcon",
135 | theme =>
136 | CSSProperties()
137 | .setWidth(30)
138 | .setMarginRight(theme.setSpacing(Spacing(2)).spacing.unit)
139 | )
140 | .add(
141 | "creatingButtonContainer",
142 | theme =>
143 | CSSProperties()
144 | .setMarginTop(theme.setSpacing(Spacing(2.5)).spacing.unit)
145 | .setHeight(46)
146 | .setDisplay(flex)
147 | .setJustifyContent(center)
148 | .setAlignItems(center)
149 | )
150 | .add(
151 | "createAccountButton",
152 | theme =>
153 | CSSProperties()
154 | .setHeight(46)
155 | .setTextTransform(none)
156 | )
157 | .add(
158 | "formDividerContainer",
159 | theme =>
160 | CSSProperties()
161 | .setMarginTop(theme.setSpacing(Spacing(4)).spacing.unit)
162 | .setMarginBottom(theme.setSpacing(Spacing(4)).spacing.unit)
163 | .setDisplay(flex)
164 | .setAlignItems(center)
165 | )
166 | .add(
167 | "formDividerWord",
168 | theme =>
169 | CSSProperties()
170 | .setPaddingLeft(theme.setSpacing(Spacing(2)).spacing.unit)
171 | .setPaddingRight(theme.setSpacing(Spacing(2)).spacing.unit)
172 | )
173 | .add(
174 | "formDivider",
175 | theme =>
176 | CSSProperties()
177 | .setFlexGrow(1)
178 | .setHeight(1)
179 | .setBackgroundColor(theme.palette.text.hint)
180 | )
181 | .add(
182 | "errorMessage",
183 | theme =>
184 | CSSProperties()
185 | .setTextAlign(center)
186 | )
187 | .add(
188 | "textFieldUnderline",
189 | theme =>
190 | CSSProperties()
191 | .set(
192 | "&:before",
193 | CSSProperties()
194 | .setBorderBottomColor(theme.palette.primary.light)
195 | )
196 | .set(
197 | "&:after",
198 | CSSProperties()
199 | .setBorderBottomColor(theme.palette.primary.main)
200 | )
201 | .set(
202 | "&:hover:before",
203 | CSSProperties()
204 | .setBorderBottomColor(s"${theme.palette.primary.light} !important")
205 | )
206 | )
207 | .add(
208 | "textField",
209 | theme =>
210 | CSSProperties()
211 | .setBorderBottomColor(theme.palette.background.default)
212 | )
213 | .add(
214 | "formButtons",
215 | theme =>
216 | CSSProperties()
217 | .setWidth("100%")
218 | .setMarginTop(theme.setSpacing(Spacing(4)).spacing.unit)
219 | .setDisplay(flex)
220 | .setJustifyContent("space-between")
221 | .setAlignItems(center)
222 | )
223 | .add(
224 | "forgetButton",
225 | theme =>
226 | CSSProperties()
227 | .setTextTransform(none)
228 | .setFontWeight(400)
229 | )
230 | .add(
231 | "loginLoader",
232 | theme =>
233 | CSSProperties()
234 | .setMarginLeft(theme.setSpacing(Spacing(4)).spacing.unit)
235 | )
236 | .add(
237 | "copyright",
238 | theme =>
239 | CSSProperties()
240 | .setMarginTop(theme.setSpacing(Spacing(4)).spacing.unit)
241 | .setWhiteSpace(nowrap)
242 | .set(
243 | theme.breakpoints.up(typings.materialUiCore.materialUiCoreStrings.md),
244 | CSSProperties()
245 | .setPosition(absolute)
246 | .setBottom(theme.setSpacing(Spacing(2)).spacing.unit)
247 | )
248 | )
249 | .hook
250 | end Styles
251 |
--------------------------------------------------------------------------------
/material-ui/src/main/scala/demo/signin/SignIn.scala:
--------------------------------------------------------------------------------
1 | package demo.signin
2 |
3 | import demo.StyleBuilder
4 | import japgolly.scalajs.react.ScalaFnComponent
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import typings.csstype.csstypeStrings.*
7 | import typings.materialUiCore.components.*
8 | import typings.materialUiCore.stylesCreateMuiThemeMod.Theme
9 | import typings.materialUiCore.materialUiCoreStrings.{contained, normal, primary, submit}
10 | import typings.materialUiCore.typographyTypographyMod.Style
11 | import typings.materialUiIcons.components as Icons
12 | import typings.materialUiStyles.makeStylesMod.StylesHook
13 | import typings.materialUiStyles.withStylesMod.{CSSProperties, Styles}
14 |
15 | import scala.scalajs.js
16 |
17 | // https://v3.material-ui.com/getting-started/page-layout-examples/sign-in/
18 | // https://github.com/mui-org/material-ui/blob/v3.x/docs/src/pages/getting-started/page-layout-examples/sign-in/SignIn.js
19 | object SignIn:
20 |
21 | lazy val styles: StylesHook[Styles[Theme, js.Object, String]] =
22 | StyleBuilder[Theme, js.Object]
23 | .add(
24 | "main",
25 | theme =>
26 | CSSProperties()
27 | .setWidth("auto")
28 | .setDisplay(block)
29 | .setMarginLeft(theme.spacing.unit * 3)
30 | .setMarginRight(theme.spacing.unit * 3)
31 | .set(
32 | theme.breakpoints.up(400 + theme.spacing.unit * 2 * 2),
33 | CSSProperties()
34 | .setWidth(400)
35 | .setMarginLeft("auto")
36 | .setMarginRight("auto")
37 | )
38 | )
39 | .add(
40 | "paper",
41 | theme =>
42 | CSSProperties()
43 | .setMarginTop(theme.spacing.unit * 8)
44 | .setDisplay(flex)
45 | .setFlexDirection(column)
46 | .setAlignItems(center)
47 | .setPadding(s"${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px")
48 | )
49 | .add(
50 | "avatar",
51 | theme =>
52 | CSSProperties()
53 | .setMargin(theme.spacing.unit)
54 | .setBackgroundColor(theme.palette.secondary.main)
55 | )
56 | .add("form", theme => CSSProperties().setWidth("100%").setMarginTop(theme.spacing.unit))
57 | .add("submit", theme => CSSProperties().setMarginTop(theme.spacing.unit * 3))
58 | .hook
59 |
60 | val component = ScalaFnComponent[Unit] { case () =>
61 | val classes = styles(js.undefined)
62 |
63 | <.main(^.className := classes("main"))(
64 | CssBaseline(),
65 | Paper.className(classes("paper"))(
66 | Avatar.className(classes("avatar"))(Icons.LockOutlined()),
67 | Typography.variant(Style.h5).component("h1")("Sign in"),
68 | <.form(^.className := classes("form"))(
69 | FormControl
70 | .margin(normal)
71 | .required(true)
72 | .fullWidth(true)(
73 | InputLabel.htmlFor("email")("Email Address"),
74 | Input.id("email").name("email").autoComplete("email").autoFocus(true)
75 | ),
76 | FormControl
77 | .margin(normal)
78 | .required(true)
79 | .fullWidth(true)(
80 | InputLabel.htmlFor("password")("Password"),
81 | Input.id("password").name("password").autoComplete("current-password")
82 | ),
83 | FormControlLabel(Checkbox.value("remember").color(primary)).label("Remember Me"),
84 | Button
85 | .`type`(submit)
86 | .fullWidth(true)
87 | .variant(contained)
88 | .color(primary)
89 | .className(classes("submit"))("Sign in")
90 | )
91 | )
92 | )
93 | }
94 | end SignIn
95 |
--------------------------------------------------------------------------------
/nivo/src/main/js/data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "japan",
4 | "color": "hsl(71, 70%, 50%)",
5 | "data": [
6 | {
7 | "x": "plane",
8 | "y": 110
9 | },
10 | {
11 | "x": "helicopter",
12 | "y": 224
13 | },
14 | {
15 | "x": "boat",
16 | "y": 190
17 | },
18 | {
19 | "x": "train",
20 | "y": 100
21 | },
22 | {
23 | "x": "subway",
24 | "y": 116
25 | },
26 | {
27 | "x": "bus",
28 | "y": 204
29 | },
30 | {
31 | "x": "car",
32 | "y": 60
33 | },
34 | {
35 | "x": "moto",
36 | "y": 192
37 | },
38 | {
39 | "x": "bicycle",
40 | "y": 137
41 | },
42 | {
43 | "x": "horse",
44 | "y": 265
45 | },
46 | {
47 | "x": "skateboard",
48 | "y": 94
49 | },
50 | {
51 | "x": "others",
52 | "y": 175
53 | }
54 | ]
55 | },
56 | {
57 | "id": "france",
58 | "color": "hsl(203, 70%, 50%)",
59 | "data": [
60 | {
61 | "x": "plane",
62 | "y": 184
63 | },
64 | {
65 | "x": "helicopter",
66 | "y": 60
67 | },
68 | {
69 | "x": "boat",
70 | "y": 295
71 | },
72 | {
73 | "x": "train",
74 | "y": 155
75 | },
76 | {
77 | "x": "subway",
78 | "y": 237
79 | },
80 | {
81 | "x": "bus",
82 | "y": 210
83 | },
84 | {
85 | "x": "car",
86 | "y": 242
87 | },
88 | {
89 | "x": "moto",
90 | "y": 288
91 | },
92 | {
93 | "x": "bicycle",
94 | "y": 178
95 | },
96 | {
97 | "x": "horse",
98 | "y": 95
99 | },
100 | {
101 | "x": "skateboard",
102 | "y": 253
103 | },
104 | {
105 | "x": "others",
106 | "y": 91
107 | }
108 | ]
109 | },
110 | {
111 | "id": "us",
112 | "color": "hsl(33, 70%, 50%)",
113 | "data": [
114 | {
115 | "x": "plane",
116 | "y": 215
117 | },
118 | {
119 | "x": "helicopter",
120 | "y": 188
121 | },
122 | {
123 | "x": "boat",
124 | "y": 45
125 | },
126 | {
127 | "x": "train",
128 | "y": 299
129 | },
130 | {
131 | "x": "subway",
132 | "y": 108
133 | },
134 | {
135 | "x": "bus",
136 | "y": 221
137 | },
138 | {
139 | "x": "car",
140 | "y": 276
141 | },
142 | {
143 | "x": "moto",
144 | "y": 25
145 | },
146 | {
147 | "x": "bicycle",
148 | "y": 5
149 | },
150 | {
151 | "x": "horse",
152 | "y": 212
153 | },
154 | {
155 | "x": "skateboard",
156 | "y": 244
157 | },
158 | {
159 | "x": "others",
160 | "y": 191
161 | }
162 | ]
163 | },
164 | {
165 | "id": "germany",
166 | "color": "hsl(53, 70%, 50%)",
167 | "data": [
168 | {
169 | "x": "plane",
170 | "y": 70
171 | },
172 | {
173 | "x": "helicopter",
174 | "y": 246
175 | },
176 | {
177 | "x": "boat",
178 | "y": 15
179 | },
180 | {
181 | "x": "train",
182 | "y": 36
183 | },
184 | {
185 | "x": "subway",
186 | "y": 182
187 | },
188 | {
189 | "x": "bus",
190 | "y": 234
191 | },
192 | {
193 | "x": "car",
194 | "y": 246
195 | },
196 | {
197 | "x": "moto",
198 | "y": 186
199 | },
200 | {
201 | "x": "bicycle",
202 | "y": 11
203 | },
204 | {
205 | "x": "horse",
206 | "y": 34
207 | },
208 | {
209 | "x": "skateboard",
210 | "y": 262
211 | },
212 | {
213 | "x": "others",
214 | "y": 209
215 | }
216 | ]
217 | },
218 | {
219 | "id": "norway",
220 | "color": "hsl(28, 70%, 50%)",
221 | "data": [
222 | {
223 | "x": "plane",
224 | "y": 166
225 | },
226 | {
227 | "x": "helicopter",
228 | "y": 70
229 | },
230 | {
231 | "x": "boat",
232 | "y": 14
233 | },
234 | {
235 | "x": "train",
236 | "y": 90
237 | },
238 | {
239 | "x": "subway",
240 | "y": 25
241 | },
242 | {
243 | "x": "bus",
244 | "y": 280
245 | },
246 | {
247 | "x": "car",
248 | "y": 276
249 | },
250 | {
251 | "x": "moto",
252 | "y": 24
253 | },
254 | {
255 | "x": "bicycle",
256 | "y": 263
257 | },
258 | {
259 | "x": "horse",
260 | "y": 1
261 | },
262 | {
263 | "x": "skateboard",
264 | "y": 210
265 | },
266 | {
267 | "x": "others",
268 | "y": 113
269 | }
270 | ]
271 | }
272 | ]
273 |
--------------------------------------------------------------------------------
/nivo/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Nivo demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/nivo/src/main/scala/demo/App.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import org.scalablytyped.runtime.StringDictionary
6 | import typings.nivoAxes.mod.{AxisProps, Orient}
7 | import typings.nivoAxes.nivoAxesStrings.middle
8 | import typings.nivoColors.mod.{ColorSchemeId, OrdinalColorsInstruction, SchemeColorInstruction}
9 | import typings.nivoCore.mod.Box
10 | import typings.nivoLegends.anon.PartialitemTextColorstrin
11 | import typings.nivoLegends.mod.*
12 | import typings.nivoLine.components.Line
13 | import typings.nivoLine.mod.Serie
14 | import typings.nivoScales.mod.{LinearScale, Scale}
15 | import typings.nivoScales.nivoScalesStrings.auto
16 |
17 | import scala.scalajs.js
18 |
19 | // ported from https://nivo.rocks/line
20 | object App:
21 | //// make sure parent container have a defined height when using
22 | //// responsive component, otherwise height will be 0 and
23 | //// no chart will be rendered.
24 | //// website examples showcase many properties,
25 | //// you'll often use just a few of them.
26 | val component = ScalaFnComponent[js.Array[Serie]] { data =>
27 | Line(data, height = 1000, width = 1500)
28 | .margin(Box().setTop(50).setRight(110).setBottom(50).setLeft(60))
29 | .xScale(Scale.PointScale())
30 | .yScale(LinearScale().setMin(auto).setMax(auto).setStacked(true).setReverse(false))
31 | .axisTopNull
32 | .axisRightNull
33 | .axisBottom(
34 | AxisProps()
35 | .setOrient(Orient.bottom)
36 | .setTickSize(5)
37 | .setTickPadding(5)
38 | .setTickRotation(0)
39 | .setLegend("transportation")
40 | .setLegendOffset(36)
41 | .setLegendPosition(middle)
42 | )
43 | .axisLeft(
44 | AxisProps()
45 | .setOrient(Orient.left)
46 | .setTickSize(5)
47 | .setTickPadding(5)
48 | .setTickRotation(0)
49 | .setLegend("count")
50 | .setLegendOffset(-40)
51 | .setLegendPosition(middle)
52 | )
53 | .colors(SchemeColorInstruction(ColorSchemeId.nivo): OrdinalColorsInstruction[Any])
54 | .pointSize(10)
55 | .pointColor(StringDictionary("theme" -> "background"))
56 | .pointBorderWidth(2)
57 | .pointBorderColor(StringDictionary("from" -> "serieColor"))
58 | .pointLabel("y")
59 | .pointLabelYOffset(-12)
60 | .useMesh(true)
61 | .legendsVarargs(
62 | LegendProps(
63 | anchor = LegendAnchor.`bottom-right`,
64 | direction = LegendDirection.column,
65 | itemHeight = 20,
66 | itemWidth = 80
67 | ).setJustify(false)
68 | .setTranslateX(100)
69 | .setTranslateY(0)
70 | .setItemsSpacing(0)
71 | .setItemDirection(LegendItemDirection.`left-to-right`)
72 | .setItemOpacity(0.75)
73 | .setSymbolSize(12)
74 | .setSymbolShape(LegendSymbolShape.circle)
75 | .setSymbolBorderColor("rgba(0, 0, 0, .5)")
76 | .setEffectsVarargs(
77 | LegendEffect(PartialitemTextColorstrin().setItemBackground("rgba(0, 0, 0, .03)").setItemOpacity(1))
78 | )
79 | )
80 | }
81 | end App
82 |
--------------------------------------------------------------------------------
/nivo/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom.document
4 | import typings.nivoLine.mod.Serie
5 | import typings.std.global.console
6 |
7 | import scala.scalajs.js
8 | import scala.scalajs.js.annotation.JSImport
9 |
10 | @js.native @JSImport("./data.json", JSImport.Namespace)
11 | val Data: js.Array[Serie] = js.native
12 |
13 | @main
14 | def main: Unit =
15 | console.warn(Data)
16 | App.component(Data).renderIntoDOM(document.getElementById("container"))
17 |
--------------------------------------------------------------------------------
/office-ui-fabric-react/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | office-ui-fabric-react demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/office-ui-fabric-react/src/main/scala/demo/Demo.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import org.scalajs.dom
6 | import typings.officeUiFabricReact.components as Fabric
7 | import typings.react.mod.useState
8 |
9 | import scala.scalajs.js
10 |
11 | val App = ScalaFnComponent[String] { name =>
12 | /* use a hook to keep state */
13 | val js.Tuple2(state, setState) = useState(1)
14 |
15 | val incrementButton = Fabric.Button.onClick(_ => Callback(setState(state + 1)))(s"Increment it, $name")
16 | val text = Fabric.TextField.value(state.toString).disabled(true)
17 | <.div(text, incrementButton)
18 | }
19 |
20 | @main
21 | def main: Unit =
22 | App("Dear user").renderIntoDOM(dom.document.getElementById("container"))
23 |
--------------------------------------------------------------------------------
/project/ScalacOptions.scala:
--------------------------------------------------------------------------------
1 | object ScalacOptions {
2 | val flags = Seq(
3 | "-deprecation", // Emit warning and location for usages of deprecated APIs.
4 | "-feature", // Emit warning and location for usages of features that should be imported explicitly.
5 | "-unchecked" // Enable additional warnings where generated code depends on assumptions.
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/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.2.1")
2 |
--------------------------------------------------------------------------------
/react-big-calendar/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-big-calendar demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/react-big-calendar/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom.document
4 | import typings.moment.{momentStrings, mod as Moment}
5 | import typings.reactBigCalendar.components.Calendar
6 | import typings.reactBigCalendar.mod.{momentLocalizer, View}
7 |
8 | import scala.scalajs.js
9 | import scala.scalajs.js.annotation.JSImport
10 |
11 | @JSImport("react-big-calendar/lib/css/react-big-calendar.css", JSImport.Namespace)
12 | @js.native
13 | object BigCalendarCss extends js.Object
14 |
15 | class Event(val start: js.Date, val end: js.Date, val title: js.UndefOr[String]) extends js.Object
16 |
17 | @main
18 | def main: Unit =
19 | BigCalendarCss // touch to load css
20 |
21 | val Localizer = momentLocalizer(Moment.^)
22 |
23 | val someEvent = new Event(
24 | start = new js.Date,
25 | end = Moment.apply(new js.Date).add(1, momentStrings.day).toDate(),
26 | title = "My amazing event"
27 | )
28 | Calendar[Event, js.Object](Localizer)
29 | .eventsVarargs(someEvent)
30 | .defaultDate(new js.Date)
31 | .defaultView(View.week)
32 | .viewsVarargs(View.agenda, View.day, View.week)
33 | .renderIntoDOM(document.getElementById("container"))
34 | end main
35 |
--------------------------------------------------------------------------------
/react-dnd/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-dnd demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/react-dnd/src/main/scala/demo/App.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.facade.React.{Element, RefFn, RefHandle}
4 | import japgolly.scalajs.react.vdom.Attr.ValueType
5 | import japgolly.scalajs.react.vdom.TopNode
6 | import japgolly.scalajs.react.vdom.html_<^.*
7 | import japgolly.scalajs.react.{Callback, CallbackTo, ScalaFnComponent}
8 | import org.scalajs.dom.HTMLElement
9 | import typings.csstype.mod.{ClearProperty, FloatProperty, TextAlignProperty}
10 | import typings.dndCore.libInterfacesMod.SourceType
11 | import typings.react.mod.CSSProperties
12 | import typings.reactDnd.components.DndProvider
13 | import typings.reactDnd.libInterfacesConnectorsMod.{ConnectDropTarget, ConnectableElement}
14 | import typings.reactDnd.libInterfacesHooksApiMod.{DragSourceHookSpec, DropTargetHookSpec}
15 | import typings.reactDnd.mod.{useDrag, useDrop}
16 | import typings.reactDndHtml5Backend.mod.HTML5Backend
17 | import typings.std.global.alert
18 |
19 | import scala.language.implicitConversions
20 | import scala.scalajs.js
21 | import scala.scalajs.js.|
22 |
23 | object components:
24 | object ItemTypes:
25 | val BOX = "box"
26 |
27 | implicit def whyyyDoesItHaveToBeSoComplicated: ValueType[HTMLElement => Callback, RefFn[HTMLElement]] =
28 | ValueType[HTMLElement => Callback, RefFn[HTMLElement]] { (consume, f) =>
29 | val massaged: RefFn[HTMLElement] =
30 | (e: HTMLElement | Null) => Option(e.asInstanceOf[HTMLElement]).foreach(e => f(e).runNow())
31 |
32 | consume(massaged)
33 | }
34 |
35 | case class Collected(isOver: Boolean, canDrop: Boolean)
36 |
37 | class DropResult(val name: String) extends js.Object
38 |
39 | val dustbinStyles = CSSProperties()
40 | .setHeight("12rem")
41 | .setWidth("12rem")
42 | .setMarginRight("1.5rem")
43 | .setMarginBottom("1.5rem")
44 | .setColor("white")
45 | .setPadding("1rem")
46 | .setTextAlign(TextAlignProperty.center)
47 | .setFontSize("1rem")
48 | .setLineHeight("normal")
49 | .setFloat(FloatProperty.left)
50 |
51 | val Dustbin = ScalaFnComponent[Unit] { case () =>
52 | val js.Tuple2(Collected(canDrop, isOver), drop) =
53 | useDrop(
54 | DropTargetHookSpec[js.Object, DropResult, Collected](ItemTypes.BOX)
55 | .setDrop((_, _) => new DropResult("Dustbin"))
56 | .setCollect(monitor => Collected(monitor.isOver(), monitor.canDrop()))
57 | )
58 |
59 | val isActive = canDrop && isOver
60 |
61 | val backgroundColor: String =
62 | if isActive then "darkgreen"
63 | else if canDrop then "darkkhaki"
64 | else "#222"
65 |
66 | <.div(
67 | ^.untypedRef(elem => drop(elem.asInstanceOf[ConnectableElement], js.undefined)),
68 | ^.style := dustbinStyles.duplicate.setBackgroundColor(backgroundColor),
69 | if isActive then "Release to drop" else "Drag a box here"
70 | )
71 | }
72 |
73 | class Dragged(val name: String, val `type`: SourceType) extends js.Object
74 |
75 | val boxStyles = CSSProperties()
76 | .setBorder("1px dashed gray")
77 | .setBackgroundColor("white")
78 | .setPadding("0.5rem 1rem")
79 | .setMarginRight("1.5rem")
80 | .setMarginBottom("1.5rem")
81 | .setCursor("move")
82 | .setFloat(FloatProperty.left)
83 |
84 | val Box = ScalaFnComponent[String] { name =>
85 | val js.Tuple3(isDragging, drag, _) =
86 | useDrag(
87 | DragSourceHookSpec[Dragged, DropResult, Boolean](item = new Dragged(name, ItemTypes.BOX))
88 | .setEnd { (itemU, monitor) =>
89 | Callback(itemU.foreach { item =>
90 | val dropResult = monitor.getDropResult()
91 | alert(s"You dropped ${item.name} into ${dropResult.asInstanceOf[DropResult].name}!")
92 | })
93 | }
94 | .setCollect(monitor => monitor.isDragging())
95 | )
96 |
97 | val opacity = if isDragging then "0.4" else "1"
98 | <.div(
99 | ^.untypedRef(elem => drag(elem.asInstanceOf[ConnectableElement], js.undefined)),
100 | ^.style := boxStyles.duplicate.setOpacity(opacity),
101 | name
102 | )
103 | }
104 | val Container = ScalaFnComponent[Unit] { case () =>
105 | <.div(
106 | <.div(^.style := CSSProperties().setOverflow("hidden").setClear(ClearProperty.both), Dustbin()),
107 | <.div(
108 | ^.style := CSSProperties().setOverflow("hidden").setClear(ClearProperty.both),
109 | Box("Glass"),
110 | Box("Banana"),
111 | Box("Paper")
112 | )
113 | )
114 | }
115 |
116 | val App = ScalaFnComponent[Unit] { case () =>
117 | <.div(^.className := "App")(
118 | DndProvider.Backend(HTML5Backend)(Container())
119 | )
120 | }
121 | end components
122 |
--------------------------------------------------------------------------------
/react-dnd/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom
4 |
5 | @main
6 | def main: Unit =
7 | components.App().renderIntoDOM(dom.document.getElementById("container"))
8 |
--------------------------------------------------------------------------------
/react-i18n/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React i18n demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/react-i18n/src/main/scala/demo/App.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import org.scalajs.dom.document
6 | import org.scalajs.dom.html.Element
7 | import typings.react.mod.CSSProperties
8 | import typings.reactI18next.components.Trans
9 | import typings.reactI18next.mod.useTranslation
10 |
11 | import scala.scalajs.js
12 |
13 | val App = ScalaFnComponent[Unit] { _ =>
14 | val js.Tuple3(t, i18n, _) = useTranslation()
15 | val index = 11
16 |
17 | // note, explicit type parameters seem to be necessary below. didn't investigate why
18 |
19 | <.div(^.className := "App")(
20 | <.div(^.className := "App-header")(
21 | // note: `t` on the line under needs type parameters in order to not run into a `ClassCastException`.
22 | // Better write a small facade around it to constrain the interface a bit if you want to use it
23 | <.h2(t[String, String, js.Object]("Welcome to React")),
24 | <.button(^.onClick --> Callback(i18n.changeLanguage(I18n.de)), "de"),
25 | <.button(^.onClick --> Callback(i18n.changeLanguage(I18n.en)), "en")
26 | ),
27 | <.div(^.className := "App-intro")(
28 | Trans[Element]()("To get started, edit ", <.code("src/App.js"), " and save to reload."),
29 | Trans[Element]().i18nKey("welcome")("trans"),
30 | Trans[Element]()(index + 1, <.a("xxx"))
31 | ),
32 | <.div(^.style := CSSProperties().setMarginTop(40))(
33 | "Learn more ",
34 | <.a(^.href := "https://react.i18next.com")("https://react.i18next.com")
35 | )
36 | )
37 | }
38 |
39 | @main
40 | def main: Unit =
41 | I18n.initialize()
42 | App().renderIntoDOM(document.getElementsByTagName("body")(0))
43 |
--------------------------------------------------------------------------------
/react-i18n/src/main/scala/demo/I18n.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalablytyped.runtime.StringDictionary
4 | import typings.i18next.i18nextBooleans.`false`
5 | import typings.i18next.mod.{InitOptions, InterpolationOptions, default as i18n}
6 | import typings.i18nextBrowserLanguagedetector.mod.default as LanguageDetector
7 | import typings.reactI18next.mod.initReactI18next
8 |
9 | import scala.scalajs.js
10 |
11 | object I18n:
12 |
13 | val namespace = "translations"
14 |
15 | val en = "en"
16 | private val enTexts = StringDictionary[js.Any](
17 | "To get started, edit <1>src/App.js1> and save to reload." -> "To get started, edit <1>src/App.js1> and save to reload.",
18 | "Welcome to React" -> "Welcome to React and react-i18next",
19 | "welcome" -> "Hello
World"
20 | )
21 |
22 | val de = "de"
23 | private val deTexts = StringDictionary[js.Any](
24 | "To get started, edit <1>src/App.js1> and save to reload." -> "Starte in dem du, <1>src/App.js1> editierst und speicherst.",
25 | "Welcome to React" -> "Willkommen bei React und react-i18next",
26 | "welcome" -> "Hello
World"
27 | )
28 |
29 | def initialize() =
30 | i18n
31 | .use(new LanguageDetector)
32 | .use(initReactI18next)
33 | .init(
34 | InitOptions()
35 | .setResources(
36 | StringDictionary(
37 | en -> StringDictionary(namespace -> enTexts),
38 | de -> StringDictionary(namespace -> deTexts)
39 | )
40 | )
41 | .setFallbackLng(en)
42 | .setDebug(true)
43 | .setDefaultNS(namespace)
44 | .setKeySeparator(`false`)
45 | .setInterpolation(InterpolationOptions().setEscapeValue(false))
46 | )
47 | end I18n
48 |
--------------------------------------------------------------------------------
/react-leaflet/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-leaflet demo
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/react-leaflet/src/main/scala/demo/App.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import typings.leaflet.mod.LatLngExpression
6 | import typings.reactLeaflet.components.{Map, Marker, Popup, TileLayer}
7 | import typings.reactLeaflet.mod.{MapProps, MarkerProps, PopupProps, TileLayerProps}
8 |
9 | import scala.language.implicitConversions
10 | import scala.scalajs.js
11 |
12 | val App = ScalaFnComponent[Unit] { _ =>
13 | val position: LatLngExpression = js.Tuple2(51.505, -0.09)
14 |
15 | Map(MapProps().setCenter(position).setZoom(13))(
16 | TileLayer(
17 | TileLayerProps(url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")
18 | .setAttribution("© OpenStreetMap contributors")
19 | ),
20 | Marker(MarkerProps(position = position))(
21 | Popup(PopupProps())("A pretty CSS3 popup.\nEasily customizable.").build
22 | )
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/react-leaflet/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom
4 |
5 | import scala.scalajs.js
6 | import scala.scalajs.js.annotation.JSImport
7 |
8 | @JSImport("./node_modules/leaflet/dist/leaflet.css", JSImport.Namespace)
9 | @js.native
10 | object Css extends js.Object
11 |
12 | @main
13 | def main: Unit =
14 | /* touch to load */
15 | typings.leaflet.leafletRequire
16 | Css
17 |
18 | App().renderIntoDOM(dom.document.getElementById("container"))
19 |
--------------------------------------------------------------------------------
/react-markdown/src/main/js/docs/README.md:
--------------------------------------------------------------------------------
1 | # Working with objects
2 |
3 | Javascript is remarkably flexible. When we integrate with arbitrary Javascript code in Scala.js, we need a very flexible
4 | encoding to tag along. The encoding chosen for ScalablyTyped is the result of years of experimentation, and has
5 | a much more dynamic feeling than what you may be used to.
6 |
7 | Let's start with an example of a type definition we want to use:
8 |
9 | ```scala
10 | @js.native
11 | trait Point extends StObject {
12 |
13 | var x: Double = js.native
14 |
15 | var y: Double = js.native
16 | }
17 | object Point {
18 |
19 | @scala.inline
20 | def apply(x: Double, y: Double): Point = {
21 | val __obj = js.Dynamic.literal(x = x.asInstanceOf[js.Any], y = y.asInstanceOf[js.Any])
22 | __obj.asInstanceOf[Point]
23 | }
24 |
25 | @scala.inline
26 | implicit class PointMutableBuilder[Self <: Point] (val x: Self) extends AnyVal {
27 |
28 | @scala.inline
29 | def setX(value: Double): Self = StObject.set(x, "x", value.asInstanceOf[js.Any])
30 |
31 | @scala.inline
32 | def setY(value: Double): Self = StObject.set(x, "y", value.asInstanceOf[js.Any])
33 | }
34 | }
35 | ```
36 |
37 | We notice several things:
38 | - it's a `@js.native` trait, so we cannot `new` it ourselves. This can be [`changed`](conversion-options.md#stenablescalajsdefined), but it's not recommended.
39 | - it has two required members (`x` and `y`). Optional members would typically be wrapped in `js.UndefOr`
40 | - it has an `object` with syntax to help us work with it
41 | - the entire syntax is built on mutability. It's Javascript, after all. more on that further down
42 |
43 | ### Basic usage
44 |
45 | ```scala
46 | // At construction time we need to supply all required parameters
47 | val p = Point(x = 1,y = 2)
48 |
49 | // we can mutate what we have
50 | // this is equivalent to typescript `p.x = 3
51 | val p2 = p.setX(3)
52 |
53 | // or we can duplicate and then mutate.
54 | // this is equivalent to typescript `const p2 = {...p, x: 3}
55 | val p3 = p.duplicate.setX(3)
56 |
57 | // we can combine with other javascript objects.
58 | // this is equivalent to javascript `const p3 = {...p, {}}`
59 | val p4: Point with TickOptions = p.combineWith(TickOptions())
60 |
61 | // fallback, if the type definitions are wrong or for any other reason you can break the contract
62 | val p5: p.duplicate.set("x", "foo")
63 |
64 | // you can also set any other property
65 | val p6: p.duplicate.set("x2", "foo")
66 | ```
67 |
--------------------------------------------------------------------------------
/react-markdown/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-markdown demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/react-markdown/src/main/scala/demo/DocPage.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.component.ScalaFn.Component
4 | import japgolly.scalajs.react.facade.React.{ElementType, Node}
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import japgolly.scalajs.react.{CtorType, ScalaFnComponent}
7 | import org.scalablytyped.runtime.StringDictionary
8 | import org.scalajs.dom.XMLHttpRequest
9 | import typings.react.mod.{useEffect, useState, EffectCallback}
10 | import typings.reactMarkdown.components.ReactMarkdown
11 | import typings.reactMarkdown.mod.{ReactMarkdownProps, ReactMarkdownPropsBase}
12 | import typings.reactSyntaxHighlighter.components.Light as SyntaxHighligther
13 | import typings.reactSyntaxHighlighter.mod.Light
14 | import typings.reactSyntaxHighlighter.distEsmStylesHljsMod.darcula
15 | import scala.scalajs.js
16 |
17 | val docFile = "./docs/README.md"
18 |
19 | class LanguageValue(val language: String, val value: String) extends js.Object
20 |
21 | val codeRender: js.Function1[LanguageValue, Node] =
22 | rp => SyntaxHighligther.style(darcula).language(rp.language)(rp.value).build.rawElement
23 |
24 | val DocPage: Component[Unit, CtorType.Nullary] = ScalaFnComponent { case () =>
25 | val js.Tuple2(document, setDocument) = useState[Option[String]](None)
26 |
27 | useEffect(
28 | (() =>
29 | val xhr = new XMLHttpRequest
30 | xhr.onload = _ => setDocument(Some(xhr.responseText))
31 | xhr.open("GET", docFile)
32 | xhr.send()
33 | ): EffectCallback,
34 | js.Array(docFile)
35 | )
36 |
37 | val props = ReactMarkdownPropsBase()
38 | .setRenderers(StringDictionary("code" -> codeRender).asInstanceOf[StringDictionary[ElementType]])
39 | .asInstanceOf[ReactMarkdownProps]
40 |
41 | ReactMarkdown(props)(document)
42 | }
43 |
--------------------------------------------------------------------------------
/react-markdown/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom
4 | import typings.reactSyntaxHighlighter.mod.Light
5 | import typings.reactSyntaxHighlighter.distEsmLanguagesHljsScalaMod.default as scalaLanguage
6 |
7 | // https://stackblitz.com/edit/react-syntax-highlighter-issue-js
8 | // https://github.com/remarkjs/react-markdown#use-custom-renderers-syntax-highlight
9 | @main
10 | def main: Unit =
11 | Light.registerLanguage("scala", scalaLanguage)
12 | DocPage().renderIntoDOM(dom.document.getElementById("container"))
13 |
--------------------------------------------------------------------------------
/react-mobx/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/react-mobx/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import org.scalajs.dom.document
4 |
5 | @main
6 | def main: Unit =
7 | TodoList(TodoListProps(new TodoStore, new PeopleStore))
8 | .renderIntoDOM(document.getElementsByTagName("body")(0))
9 |
--------------------------------------------------------------------------------
/react-mobx/src/main/scala/demo/ObserverComponent.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.component.JsFn
4 | import japgolly.scalajs.react.component.ScalaFn.Component
5 | import japgolly.scalajs.react.internal.Box
6 | import japgolly.scalajs.react.vdom.VdomElement
7 | import japgolly.scalajs.react.{facade, Children, CtorType, PropsChildren}
8 | import typings.mobxReact.mod.observer
9 |
10 | import scala.scalajs.js
11 |
12 | object ObserverComponent:
13 | private def create[P, C <: Children, CT[-p, +u] <: CtorType[p, u]](
14 | render: Box[P] with facade.PropsWithChildren => VdomElement
15 | )(implicit s: CtorType.Summoner.Aux[Box[P], C, CT]): Component[P, CT] =
16 |
17 | val jsRender =
18 | render.andThen(_.rawElement): js.Function1[Box[P] with facade.PropsWithChildren, facade.React.Element]
19 | val rawComponent = observer(jsRender).asInstanceOf[facade.React.StatelessFunctionalComponent[Box[P]]]
20 |
21 | JsFn
22 | .force[Box[P], C](rawComponent)(s)
23 | .cmapCtorProps[P](Box(_))
24 | .mapUnmounted(_.mapUnmountedProps(_.unbox))
25 | end create
26 |
27 | def apply[P](render: P => VdomElement)(implicit s: CtorType.Summoner[Box[P], Children.None]): Component[P, s.CT] =
28 | create[P, Children.None, s.CT](b => render(b.unbox))(s)
29 |
30 | def withChildren[P](
31 | render: (P, PropsChildren) => VdomElement
32 | )(implicit s: CtorType.Summoner[Box[P], Children.Varargs]): Component[P, s.CT] =
33 | create[P, Children.Varargs, s.CT](b => render(b.unbox, PropsChildren(b.children)))(s)
34 |
35 | def justChildren(render: PropsChildren => VdomElement): Component[Unit, CtorType.Children] =
36 | create(b => render(PropsChildren(b.children)))
37 | end ObserverComponent
38 |
--------------------------------------------------------------------------------
/react-mobx/src/main/scala/demo/PeopleStore.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import typings.mobx.mod as MobX
4 | import typings.mobx.libTypesObservablevalueMod.IObservableValue
5 |
6 | import scala.scalajs.js
7 |
8 | case class Person(name: String):
9 | def changeName(n: String): Person = Person(n)
10 |
11 | class PeopleStore:
12 |
13 | val people: IObservableValue[List[Person]] =
14 | MobX.observable.box(List(Person("Michel"), Person("Me")))
15 |
16 | def changePeople(f: List[Person] => List[Person]): Unit = people.set(f(people.get()))
17 |
18 | def updatePerson(index: Int, f: Person => Person): Unit = changePeople(_.updated(index, f(people.get()(index))))
19 |
20 | val renamePerson: js.Function2[Int, String, Unit] =
21 | MobX.action("renamePerson", (index: Int, name: String) => updatePerson(index, _.changeName(name)))
22 | end PeopleStore
23 |
--------------------------------------------------------------------------------
/react-mobx/src/main/scala/demo/TodoList.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, CallbackTo}
5 | import org.scalajs.dom.window
6 | import typings.react.mod.useEffect
7 |
8 | import scala.scalajs.js
9 |
10 | case class TodoListProps(store: TodoStore, peopleStore: PeopleStore)
11 |
12 | val TodoList = ObserverComponent { case TodoListProps(store, peopleStore) =>
13 | useEffect(
14 | () =>
15 | store.assignTodo(0, Some(peopleStore.people.get()(0)))
16 | store.assignTodo(1, Some(peopleStore.people.get()(1)))
17 | peopleStore.renamePerson(0, "Michel Weststrate")
18 | ,
19 | js.Array() // run an effect and clean it up only once (on mount and unmount)
20 | )
21 |
22 | val onNewTodo: Callback = CallbackTo(window.prompt("Task name", "coffee plz")) flatMap {
23 | case e if e.isEmpty => Callback.empty
24 | case task => Callback(store.addTodo(task))
25 | }
26 |
27 | val onLoadTodo = Callback {
28 | store.increasePending()
29 | window.setTimeout(
30 | () =>
31 | store.addTodo("Random Todo " + Math.random())
32 | store.decreasePending()
33 | ,
34 | 2000
35 | )
36 | }
37 |
38 | val todoViews = TagMod.fromTraversableOnce {
39 | val ts = store.todos.get().todos
40 | ts.indices.map(index =>
41 | TodoView.withKey("td" + index)(
42 | TodoViewProps(
43 | todo = ts(index),
44 | toggle = Callback(store.toggleTodo(index)),
45 | rename = task => Callback(store.renameTodo(index, task))
46 | )
47 | )
48 | )
49 | }
50 |
51 | <.div(
52 | store.report.get(),
53 | <.ul(todoViews),
54 | <.ul(
55 | store.pendingRequests.get().c match
56 | case 0 => <.div()
57 | case _ => <.li("Loading...")
58 | ),
59 | <.button(^.onClick --> onNewTodo)("New Todo"),
60 | <.small("double-click a todo to edit"),
61 | <.div(
62 | <.button(^.onClick --> onLoadTodo.void)("Load async Todo")
63 | )
64 | )
65 | }
66 |
--------------------------------------------------------------------------------
/react-mobx/src/main/scala/demo/TodoStore.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import typings.mobx.libCoreComputedvalueMod.IComputedValue
4 | import typings.mobx.mod as MobX
5 | import typings.mobx.libTypesObservablevalueMod.IObservableValue
6 |
7 | import scala.scalajs.js
8 |
9 | case class Todo(task: String, completed: Boolean = false, assignee: Option[Person] = None):
10 |
11 | def renameTask(t: String): Todo = Todo(t, completed, assignee)
12 |
13 | def toggleCompleted: Todo = Todo(task, !completed, assignee)
14 |
15 | def changeAssignee(a: Option[Person]): Todo = Todo(task, completed, a)
16 |
17 | object Todo:
18 |
19 | def fromTask(task: String): Todo = Todo(task)
20 |
21 | case class Todos(todos: List[Todo]):
22 |
23 | def completedCount(): Int = todos.count(_.completed == true)
24 |
25 | def findUncompleted(): Option[Todo] = todos.find(_.completed == false)
26 |
27 | def addNewTask(task: String): Todos = Todos(todos :+ Todo.fromTask(task))
28 |
29 | case class PendingRequests(c: Int):
30 |
31 | def increase: PendingRequests = PendingRequests(c + 1)
32 |
33 | def decrease: PendingRequests = PendingRequests(c - 1)
34 |
35 | class TodoStore:
36 |
37 | val todos: IObservableValue[Todos] =
38 | MobX.observable.box(Todos(List(Todo("read MobX tutorial"), Todo("try MobX"))))
39 |
40 | val pendingRequests: IObservableValue[PendingRequests] =
41 | MobX.observable.box(PendingRequests(0))
42 |
43 | val report: IComputedValue[String] =
44 | MobX.computed { () =>
45 | val ts = todos.get()
46 | "Next todo: " + (ts.findUncompleted() match
47 | case Some(nextTodo) => nextTodo.task
48 | case None => ""
49 | ) + ". Progress: " + ts.completedCount() + "/" + ts.todos.length
50 | }
51 |
52 | def changeTodos(f: Todos => Todos): Unit = todos.set(f(todos.get()))
53 |
54 | def updateTodo(index: Int, f: Todo => Todo): Unit =
55 | changeTodos(t => Todos(t.todos.updated(index, f(todos.get().todos(index)))))
56 |
57 | val addTodo: js.Function1[String, Unit] =
58 | MobX.action("addTodo", (task: String) => changeTodos(_.addNewTask(task)))
59 |
60 | val toggleTodo: js.Function1[Int, Unit] =
61 | MobX.action("toggleTodo", (index: Int) => updateTodo(index, _.toggleCompleted))
62 |
63 | val renameTodo: js.Function2[Int, String, Unit] =
64 | MobX.action("renameTodo", (index: Int, task: String) => updateTodo(index, _.renameTask(task)))
65 |
66 | val assignTodo: js.Function2[Int, Option[Person], Unit] =
67 | MobX.action("assignTodo", (index: Int, assignee: Option[Person]) => updateTodo(index, _.changeAssignee(assignee)))
68 |
69 | def changeRequests(f: PendingRequests => PendingRequests): Unit = pendingRequests.set(f(pendingRequests.get()))
70 |
71 | val increasePending: js.Function0[Unit] =
72 | MobX.action("increasePending", () => changeRequests(_.increase))
73 |
74 | val decreasePending: js.Function0[Unit] =
75 | MobX.action("decreasePending", () => changeRequests(_.decrease))
76 | end TodoStore
77 |
--------------------------------------------------------------------------------
/react-mobx/src/main/scala/demo/TodoView.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, CallbackTo}
5 | import org.scalajs.dom.window
6 |
7 | case class TodoViewProps(todo: Todo, toggle: Callback, rename: String => Callback)
8 |
9 | val TodoView = ObserverComponent { case TodoViewProps(todo, toggle, rename) =>
10 | val onRename = CallbackTo(window.prompt("Task name", todo.task)) flatMap {
11 | case e if e.isEmpty => Callback.empty
12 | case task => rename(task)
13 | }
14 |
15 | <.li(^.onDoubleClick --> onRename)(
16 | <.input(
17 | ^.`type` := "checkbox",
18 | ^.checked := todo.completed,
19 | ^.onChange --> toggle
20 | ),
21 | todo.task,
22 | todo.assignee match
23 | case Some(person) => <.small(person.name)
24 | case None => <.span()
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/react-native/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Text, View} from 'react-native';
3 |
4 | if (__DEV__) {
5 | module.exports = require("./target/scala-2.13/react-native-fastopt.js").app;
6 | } else {
7 | module.exports = () => {
8 | return
9 |
10 | Scala.js opt mode has not been enabled in App.js, please uncomment the code for it.
11 |
12 | ;
13 | }
14 |
15 | // uncomment the following line to enable opt building
16 | // module.exports = require("./target/scala-2.13/react-native-opt.js").app;
17 | }
18 |
--------------------------------------------------------------------------------
/react-native/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "React Native basic example",
4 | "description": "React Native example with react-router and antd",
5 | "slug": "react-native",
6 | "privacy": "public",
7 | "sdkVersion": "37.0.0",
8 | "platforms": [
9 | "ios",
10 | "android"
11 | ],
12 | "packagerOpts": {
13 | "config": "metro.config.js"
14 | },
15 | "version": "1.0.0",
16 | "orientation": "portrait",
17 | "icon": "./assets/icon.png",
18 | "splash": {
19 | "image": "./assets/splash.png",
20 | "resizeMode": "contain",
21 | "backgroundColor": "#ffffff"
22 | },
23 | "updates": {
24 | "fallbackToCacheTimeout": 0
25 | },
26 | "assetBundlePatterns": [
27 | "**/*"
28 | ],
29 | "ios": {
30 | "supportsTablet": true
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/react-native/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScalablyTyped/ScalaJsReactDemos/ee2c2dfad18ffe29f7ad092a20ee5aa4c0eca841/react-native/assets/icon.png
--------------------------------------------------------------------------------
/react-native/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScalablyTyped/ScalaJsReactDemos/ee2c2dfad18ffe29f7ad092a20ee5aa4c0eca841/react-native/assets/splash.png
--------------------------------------------------------------------------------
/react-native/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/react-native/fastopt-noparse.js:
--------------------------------------------------------------------------------
1 | var transformer = require('metro-react-native-babel-transformer')
2 |
3 | module.exports.transform = function ({ src, filename, options }) {
4 | options = options || {};
5 | if (filename.indexOf('fastopt') > -1) {
6 | return {
7 | code: src,
8 | filename
9 | }
10 | }
11 | else {
12 | return transformer.transform({ src, filename, options });
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/react-native/metro.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transformer: {
3 | babelTransformerPath: require("path").resolve("./fastopt-noparse.js"),
4 | getTransformOptions: async () => ({
5 | transform: {
6 | experimentalImportSupport: false,
7 | inlineRequires: false,
8 | },
9 | }),
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/react-native/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject"
9 | },
10 | "dependencies": {
11 | "@ant-design/icons-react-native": "2.0.0",
12 | "@ant-design/react-native": "3.3.0",
13 | "@types/node": "^13.13.2",
14 | "@types/react": "16.9.34",
15 | "@types/react-native": "^0.62.4",
16 | "@types/react-router-native": "^5.1.0",
17 | "expo": "^37.0.7",
18 | "expo-font": "8.1.1",
19 | "react": "16.9.0",
20 | "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
21 | "react-router-native": "5.1.2",
22 | "typescript": "3.8"
23 | },
24 | "devDependencies": {
25 | "@babel/core": "^7.9.0",
26 | "babel-preset-expo": "^8.1.0",
27 | "react-proxy": "^1.1.8"
28 | },
29 | "private": true
30 | }
31 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/Antd.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, CallbackTo, ScalaFnComponent}
5 | import typings.antDesignIconsReactNative.antDesignIconsReactNativeStrings.{flag, gift}
6 | import typings.antDesignIconsReactNative.components.{IconFill, IconOutline}
7 | import typings.antDesignReactNative.antDesignReactNativeStrings as antdStrings
8 | import typings.antDesignReactNative.components.{List as AntdList, *}
9 | import typings.antDesignReactNative.mod.Toast
10 | import typings.antDesignReactNative.libModalPropsTypeMod.Action
11 | import typings.react.mod.useState
12 | import typings.reactNative.components.ScrollView
13 | import typings.reactNative.mod.{FlexAlignType, ViewStyle}
14 | import typings.reactNative.reactNativeStrings
15 |
16 | import scala.scalajs.js
17 |
18 | val Antd = ScalaFnComponent[Unit] { case () =>
19 | val js.Tuple2(isModalVisible, updateIsModalVisible) = useState(false)
20 |
21 | View(
22 | Text.style(Styles.title)("Antd components"),
23 | ScrollView
24 | .automaticallyAdjustContentInsets(false)
25 | .showsHorizontalScrollIndicator(false)
26 | .showsVerticalScrollIndicator(false)(
27 | AntdList.renderHeaderVdomElement(Text("List header"))(
28 | ListItem
29 | .arrow(antdStrings.horizontal)
30 | .onPress(e => Callback(updateIsModalVisible(true)))("Open modal"),
31 | ListItem
32 | .arrow(antdStrings.horizontal)
33 | .onPress(e => Callback(Toast.success("Successful!")))(
34 | "Launch success toast"
35 | )
36 | ),
37 | View.style(
38 | ViewStyle()
39 | .setBackgroundColor("white")
40 | .setFlex(1)
41 | .setFlexDirection(reactNativeStrings.column)
42 | .setJustifyContent(reactNativeStrings.center)
43 | .setAlignItems(FlexAlignType.center)
44 | )(
45 | InputItem.placeholder("input text"),
46 | InputItem
47 | .placeholder("password")
48 | .`type`(antdStrings.password)
49 | .error(true)
50 | .onErrorClick(_ => Callback(Toast.fail("Always wrong!")))
51 | .last(true),
52 | WingBlank.size(antdStrings.lg)(
53 | Button
54 | .onPress(_ => Callback(Toast.fail("Failure!")))
55 | .`type`(antdStrings.primary)("Launch fail toast")
56 | ),
57 | WhiteSpace.size(antdStrings.xl),
58 | IconFill(flag).size(40),
59 | IconOutline(name = gift).size(80),
60 | Icon(name = "experiment").size(antdStrings.lg).color("#A82")
61 | )
62 | ),
63 | Modal(visible = isModalVisible)
64 | .transparent(true)
65 | .maskClosable(true)
66 | .closable(false)
67 | .title("Basic modal")
68 | .onClose(Callback(updateIsModalVisible(false)))
69 | .footer(
70 | js.Array(
71 | Action("Cancel").setOnPress(CallbackTo(updateIsModalVisible(false))),
72 | Action("OK").setOnPress(CallbackTo(updateIsModalVisible(false)))
73 | )
74 | )
75 | )(Text("Some contents..."))
76 | }
77 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/App.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import japgolly.scalajs.react.component.ScalaFn.Component
4 | import japgolly.scalajs.react.facade.React
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import japgolly.scalajs.react.{Callback, CtorType, ReactEventFrom, ScalaFnComponent}
7 | import org.scalajs.dom.Element
8 | import typings.antDesignReactNative.anon.PartialLocale
9 | import typings.antDesignReactNative.antDesignReactNativeStrings.xl
10 | import typings.antDesignReactNative.components.{List as AntdList, *}
11 | import typings.bang88ReactNativeDrawerLayout.mod.DrawerLayout
12 | import typings.react.mod.useState
13 | import typings.reactNative.components.ScrollView
14 | import typings.reactNative.mod.NodeHandle
15 | import typings.reactRouter.components.Route
16 | import typings.reactRouter.mod.{ExtractRouteParams, RouteProps}
17 | import typings.reactRouterNative.components.{NativeRouter, Redirect}
18 |
19 | import scala.scalajs.js
20 | import scala.scalajs.js.|
21 |
22 | sealed abstract class RoutePath(val path: String, val title: String)
23 |
24 | object RoutePath:
25 | object Home extends RoutePath("/", "Home")
26 | object Antd extends RoutePath("/antd", "Antd")
27 | object ReactRouter extends RoutePath("/react_router", "React Router")
28 |
29 | val allOrdered: List[RoutePath] = List(Home, Antd, ReactRouter)
30 |
31 | def toOption[T](ot: T | Null): Option[T] =
32 | Option(ot.asInstanceOf[T])
33 |
34 | val App: Component[Unit, CtorType.Nullary] = ScalaFnComponent { case () =>
35 | var ref: Option[DrawerLayout] = None
36 |
37 | val js.Tuple2(redirPath, updateRedirPath) = useState(RoutePath.Home.path)
38 |
39 | def navigateTo(route: RoutePath)(e: ReactEventFrom[NodeHandle with Element]) =
40 | Callback {
41 | updateRedirPath(route.path)
42 | ref.foreach(_.closeDrawer())
43 | }
44 |
45 | def checkRedirection(stayPath: String, elem: VdomElement): VdomNode =
46 | if redirPath != stayPath then Redirect(to = redirPath) else elem
47 |
48 | val routeLinks: Seq[VdomElement] = RoutePath.allOrdered.zipWithIndex.map { case (route, index) =>
49 | ListItem.onPress(navigateTo(route))(Text(route.title)).withKey(index.toString)
50 | }
51 |
52 | Provider.locale(PartialLocale().setLocale("enUS"))(
53 | NativeRouter(
54 | Drawer
55 | .drawerRef(nullableRef => Callback { ref = toOption(nullableRef) })
56 | .sidebar(ScrollView(WhiteSpace.size(xl), AntdList(routeLinks*)))(
57 | AntdList.renderHeaderVdomElement(WhiteSpace.size(xl))(
58 | ListItem
59 | .extra(Icon(name = "menu"))
60 | .onPress(_ => Callback(ref.foreach(_.openDrawer())))("React Native demo")
61 | )
62 | ),
63 | Route(
64 | RouteProps[String, ExtractRouteParams[String, String]]()
65 | .setPath(RoutePath.Home.path)
66 | .setRender(props => checkRedirection(props.`match`.path, Home()).rawNode)
67 | .setExact(true)
68 | ),
69 | Route(
70 | RouteProps()
71 | .setPath(RoutePath.Antd.path)
72 | .setRender(props => checkRedirection(props.`match`.path, Antd()).rawNode)
73 | ),
74 | Route(
75 | RouteProps[String, ExtractRouteParams[String, String]]()
76 | .setPath(RoutePath.ReactRouter.path)
77 | .setRender(props => checkRedirection(props.`match`.path, ReactRouter(props.`match`)).rawNode)
78 | .setExact(true)
79 | )
80 | )
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/Home.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import japgolly.scalajs.react.component.ScalaFn.Component
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import japgolly.scalajs.react.{CtorType, ScalaFnComponent}
6 | import typings.reactNative.components.{Text, View}
7 | import typings.reactNative.mod.{TextStyle, ViewStyle}
8 |
9 | val Home: Component[Unit, CtorType.Nullary] = ScalaFnComponent { case () =>
10 | View.style(ViewStyle().setMargin(20))(
11 | Text.style(TextStyle().setFontSize(16))(
12 | "This is a demo written in Scala through Scala.js, Slinky, ScalablyTyped and Expo.\n\n" +
13 | "It uses components from Antd Native and React Router Native."
14 | )
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/LoadFonts.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.Implicits.*
5 | import org.scalablytyped.runtime.StringDictionary
6 | import typings.expo.components.AppLoading
7 | import typings.expoFont.buildFontDottypesMod.FontSource
8 | import typings.expoFont.mod as Font
9 | import typings.react.mod.{useEffect, useState}
10 | import typings.reactNative.components.Text
11 |
12 | import scala.concurrent.ExecutionContext.Implicits.global
13 | import scala.scalajs.js
14 | import scala.scalajs.js.annotation.JSImport
15 | import scala.util.{Failure, Success}
16 |
17 | /* we must load these fonts manually to use antd design */
18 | object LoadFonts:
19 | object Fonts:
20 | @JSImport("../../node_modules/@ant-design/icons-react-native/fonts/antoutline.ttf", JSImport.Namespace)
21 | @js.native
22 | val AntdIconOutline: FontSource = js.native
23 |
24 | @JSImport("../../node_modules/@ant-design/icons-react-native/fonts/antfill.ttf", JSImport.Namespace)
25 | @js.native
26 | val AntdIconFill: FontSource = js.native
27 |
28 | val All: StringDictionary[FontSource] = StringDictionary(
29 | "antoutline" -> AntdIconOutline,
30 | "antfill" -> AntdIconFill
31 | )
32 | end Fonts
33 |
34 | sealed trait State
35 | object State:
36 | case object Loading extends State
37 | case class Error(msg: String) extends State
38 | case object Success extends State
39 |
40 | val component = ScalaFnComponent[Unit] { case () =>
41 | val js.Tuple2(state, setState) = useState(State.Loading: State)
42 |
43 | useEffect(
44 | () =>
45 | Font
46 | .loadAsync(Fonts.All)
47 | .toFuture
48 | .onComplete {
49 | case Failure(exception) => setState(State.Error(exception.getMessage))
50 | case Success(_) => setState(State.Success)
51 | },
52 | js.Array()
53 | )
54 |
55 | state match
56 | case State.Loading => AppLoading.AutoHideSplash()
57 | case State.Error(msg) => Text(s"Could not load fonts: $msg")
58 | case State.Success => App()
59 | }
60 | end LoadFonts
61 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/Main.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import scala.scalajs.js.annotation.JSExportTopLevel
4 |
5 | @JSExportTopLevel("app")
6 | val app = LoadFonts.component.toJsComponent.raw
7 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/ReactRouter.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import typings.reactNative.components.{Text, View}
6 | import typings.reactRouter.mod.{Route as _, *}
7 | import typings.reactRouterNative.components.*
8 |
9 | val ReactRouter = ScalaFnComponent[`match`[?]] { m =>
10 | def link(title: String, path: String): VdomElement =
11 | Link(to = m.url + path).style(Styles.subNavItemStyle)(Text.style(Styles.topicStyle)(title))
12 |
13 | View(
14 | Text.style(Styles.title)("React Router demo"),
15 | Text.style(Styles.headerStyle)("Topics"),
16 | View(
17 | link("Rendering with React", "/rendering"),
18 | link("Components", "/components"),
19 | link("Props v. State", "/props-v-state")
20 | ),
21 | Route(
22 | RouteProps()
23 | .setPath(m.path + "/:topicId")
24 | .setRender(props => Topic(props.`match`.asInstanceOf[`match`[Param]]).rawNode)
25 | ),
26 | Route(RouteProps().setPath(m.path).setRender(_ => Text("Please select a topic").rawNode))
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/Styles.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import typings.reactNative.mod.TextStyle
4 | import typings.reactNative.reactNativeStrings
5 |
6 | object Styles:
7 | val headerStyle = TextStyle()
8 | .setPadding(10)
9 | .setFontSize(20)
10 | .setColor("black")
11 |
12 | val topicStyle = TextStyle()
13 | .setPaddingTop(30)
14 | .setTextAlign(reactNativeStrings.center)
15 | .setFontSize(15)
16 |
17 | val subNavItemStyle = TextStyle().setPadding(5)
18 |
19 | val title = TextStyle()
20 | .setPadding(10)
21 | .setFontSize(20)
22 | .setFontWeight(reactNativeStrings.bold)
23 | .setTextAlign(reactNativeStrings.center)
24 | .setColor("red")
25 | end Styles
26 |
--------------------------------------------------------------------------------
/react-native/src/main/scala/hello/world/Topic.scala:
--------------------------------------------------------------------------------
1 | package hello.world
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.Implicits.*
5 | import typings.reactNative.components.Text
6 | import typings.reactRouter.mod.*
7 |
8 | import scala.scalajs.js
9 |
10 | trait Param extends js.Object:
11 | val topicId: String
12 |
13 | val Topic = ScalaFnComponent[`match`[Param]] { m =>
14 | Text.style(Styles.topicStyle)("Topic: " + m.params.topicId)
15 | }
16 |
--------------------------------------------------------------------------------
/react-redux/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-redux demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import demo.advanced.{ExpenseAction, ExpenseContainer, ExpenseReducer, ExpenseState}
4 | import demo.basic.CakeAction.CakeAction
5 | import demo.basic.{CakeContainer, CakeReducer}
6 | import japgolly.scalajs.react.{Children, JsComponent}
7 | import org.scalajs.dom
8 | import typings.reactRedux.components.Provider
9 | import typings.reactRedux.mod.connect
10 | import typings.redux.mod.*
11 | import typings.reduxDevtoolsExtension.developmentOnlyMod.composeWithDevTools
12 |
13 | import scala.scalajs.js
14 | import scala.scalajs.js.|
15 |
16 | object Main:
17 | type AppAction = ExpenseAction | CakeAction
18 |
19 | trait AppState extends js.Object:
20 | val expenses: ExpenseState
21 | val cake: CakeReducer.State
22 |
23 | trait Reducers extends js.Object:
24 | val expenses: Reducer[ExpenseState, ExpenseAction]
25 | val cake: Reducer[CakeReducer.State, CakeAction]
26 |
27 | val rootReducer: Reducer[AppState, AppAction] =
28 | combineReducers(new Reducers:
29 | override val expenses: Reducer[ExpenseState, ExpenseAction] = ExpenseReducer.Reducer
30 | override val cake: Reducer[CakeReducer.State, CakeAction] = CakeReducer.Reducer
31 | ).asInstanceOf[Reducer[AppState, AppAction]]
32 |
33 | def main(args: Array[String]): Unit =
34 | val Empty = js.Object
35 |
36 | val store = createStore(rootReducer, composeWithDevTools(applyMiddleware()))
37 | val keepDispatch: js.Function1[Dispatch[AppAction], js.Dynamic] = d => js.Dynamic.literal(dispatch = d)
38 |
39 | val ConnectedExpenses = JsComponent[js.Object, Children.None, js.Object](
40 | connect
41 | .asInstanceOf[js.Dynamic]((appState: AppState) => js.Dynamic.literal(state = appState.expenses), keepDispatch)(
42 | ExpenseContainer.component.toJsComponent.raw
43 | )
44 | )
45 |
46 | val ConnectedCakes = JsComponent[js.Object, Children.None, js.Object](
47 | connect.asInstanceOf[js.Dynamic]((appState: AppState) => js.Dynamic.literal(state = appState.cake), keepDispatch)(
48 | CakeContainer.component.toJsComponent.raw
49 | )
50 | )
51 |
52 | Provider(store.unsafeCast2[Any, AppAction])(
53 | ConnectedExpenses(Empty),
54 | ConnectedCakes(Empty)
55 | ).renderIntoDOM(dom.document.getElementById("container"))
56 | end main
57 | end Main
58 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/advanced/Expense.scala:
--------------------------------------------------------------------------------
1 | package demo.advanced
2 |
3 | import java.util.UUID
4 | import scala.scalajs.js
5 |
6 | class Expense(val id: String, val description: String, val note: String, val amount: String, val createAt: String)
7 | extends js.Object
8 |
9 | object Expense:
10 |
11 | val default = new Expense("", "", "", "", "")
12 |
13 | def apply(): Expense = new Expense(UUID.randomUUID().toString, "description", "note", "1.0", "createAt")
14 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/advanced/ExpenseAction.scala:
--------------------------------------------------------------------------------
1 | package demo.advanced
2 |
3 | import typings.redux.mod.Action
4 |
5 | import scala.scalajs.js
6 |
7 | @js.native
8 | sealed trait ExpenseAction extends Action[String]
9 |
10 | object ExpenseAction:
11 |
12 | @js.native
13 | trait SetExpenseAction extends ExpenseAction:
14 | val expenses: js.Array[Expense] = js.native
15 |
16 | object SetExpenseAction:
17 | def _type = "SET_EXPENSE"
18 |
19 | @scala.inline
20 | def apply(expenses: js.Array[Expense]): SetExpenseAction =
21 | val __obj = js.Dynamic.literal()
22 | __obj.updateDynamic("type")(_type)
23 | __obj.asInstanceOf[SetExpenseAction].set("expenses", expenses)
24 | def unapply(a: Action[String]): Option[js.Array[Expense]] =
25 | if a.`type` == _type then Some(a.asInstanceOf[SetExpenseAction].expenses) else None
26 | end SetExpenseAction
27 |
28 | @js.native
29 | trait EditExpenseAction extends ExpenseAction:
30 | val expense: Expense = js.native
31 |
32 | object EditExpenseAction:
33 | def _type = "EDIT_EXPENSE"
34 |
35 | @scala.inline
36 | def apply(expense: Expense): EditExpenseAction =
37 | val __obj = js.Dynamic.literal()
38 | __obj.updateDynamic("type")(_type)
39 | __obj.asInstanceOf[EditExpenseAction].set("expense", expense)
40 | def unapply(a: Action[String]): Option[Expense] =
41 | if a.`type` == _type then Some(a.asInstanceOf[EditExpenseAction].expense) else None
42 | end EditExpenseAction
43 |
44 | @js.native
45 | trait RemoveExpenseAction extends ExpenseAction:
46 | val id: String = js.native
47 |
48 | object RemoveExpenseAction:
49 | def _type = "REMOVE_EXPENSE"
50 |
51 | @scala.inline
52 | def apply(id: String): RemoveExpenseAction =
53 | val __obj = js.Dynamic.literal("id" -> id)
54 | __obj.updateDynamic("type")(_type)
55 | __obj.asInstanceOf[RemoveExpenseAction]
56 |
57 | def unapply(a: Action[String]): Option[String] =
58 | if a.`type` == _type then Some(a.asInstanceOf[RemoveExpenseAction].id) else None
59 | end RemoveExpenseAction
60 |
61 | @js.native
62 | trait AddExpenseAction extends ExpenseAction:
63 | val expense: Expense = js.native
64 |
65 | object AddExpenseAction:
66 | def _type = "ADD_EXPENSE"
67 | @scala.inline
68 | def apply(expense: Expense): AddExpenseAction =
69 | val __obj = js.Dynamic.literal("expense" -> expense)
70 | __obj.updateDynamic("type")(_type)
71 | __obj.asInstanceOf[AddExpenseAction]
72 |
73 | def unapply(a: Action[String]): Option[Expense] =
74 | if a.`type` == _type then Some(a.asInstanceOf[AddExpenseAction].expense) else None
75 | end AddExpenseAction
76 | end ExpenseAction
77 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/advanced/ExpenseContainer.scala:
--------------------------------------------------------------------------------
1 | package demo.advanced
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import typings.redux.mod.Dispatch
6 |
7 | import scala.scalajs.js
8 |
9 | // https://www.youtube.com/watch?v=OXxul6AvXNs
10 | // https://github.com/cmcaboy/redux-typed/tree/typed
11 | object ExpenseContainer:
12 |
13 | @js.native
14 | trait Props extends js.Object:
15 | val state: ExpenseState
16 | val dispatch: Dispatch[ExpenseAction]
17 |
18 | val component = ScalaFnComponent[Props] { (props: Props) =>
19 | <.div(
20 | <.h1(s"Expense Page"),
21 | <.div(
22 | props.state.expenses
23 | .map(expense =>
24 | <.div(^.key := expense.id)(
25 | <.p(expense.description),
26 | <.p(expense.amount),
27 | <.p(expense.note),
28 | <.button(^.onClick --> Callback(props.dispatch(ExpenseAction.RemoveExpenseAction(expense.id))))(
29 | "Remove Expense"
30 | ),
31 | <.button(^.onClick --> Callback(props.dispatch(ExpenseAction.EditExpenseAction(expense))))("Edit Expense")
32 | )
33 | )
34 | .toVdomArray
35 | ),
36 | <.button(^.onClick --> Callback(props.dispatch(ExpenseAction.SetExpenseAction(js.Array(Expense())))))(
37 | "Set Expense"
38 | ),
39 | <.button(^.onClick --> Callback(props.dispatch(ExpenseAction.AddExpenseAction(Expense()))))("Add Expense")
40 | )
41 | }
42 | end ExpenseContainer
43 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/advanced/ExpenseReducer.scala:
--------------------------------------------------------------------------------
1 | package demo.advanced
2 |
3 | import typings.redux.mod.*
4 |
5 | object ExpenseReducer:
6 | val Reducer: Reducer[ExpenseState, ExpenseAction] = (stateOpt, action) =>
7 | val state = stateOpt.getOrElse(ExpenseState.initial)
8 | action match
9 | case ExpenseAction.SetExpenseAction(expenses) =>
10 | ExpenseState(expenses)
11 |
12 | case ExpenseAction.EditExpenseAction(editedExpense) =>
13 | ExpenseState(state.expenses.map { expense =>
14 | if expense.id.equals(editedExpense.id) then editedExpense
15 | else expense
16 | })
17 |
18 | case ExpenseAction.RemoveExpenseAction(expenseId) =>
19 | ExpenseState(state.expenses.filterNot(_.id.equals(expenseId)))
20 |
21 | case ExpenseAction.AddExpenseAction(expense) =>
22 | ExpenseState(state.expenses :+ expense)
23 | case _ =>
24 | state
25 | end match
26 | end ExpenseReducer
27 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/advanced/ExpenseState.scala:
--------------------------------------------------------------------------------
1 | package demo.advanced
2 |
3 | import scala.scalajs.js
4 |
5 | class ExpenseState(val expenses: js.Array[Expense]) extends js.Object
6 |
7 | object ExpenseState:
8 |
9 | val initial: ExpenseState = ExpenseState(js.Array[Expense]())
10 |
11 | def apply(expenses: js.Array[Expense]): ExpenseState =
12 | new ExpenseState(expenses)
13 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/basic/CakeAction.scala:
--------------------------------------------------------------------------------
1 | package demo.basic
2 |
3 | import typings.redux.mod.Action
4 |
5 | import scala.scalajs.js
6 |
7 | object CakeAction:
8 |
9 | @js.native
10 | sealed trait CakeAction extends Action[String]
11 |
12 | @js.native
13 | trait BuyCake extends CakeAction
14 |
15 | object BuyCake:
16 | val _type = "BUY_CAKE"
17 |
18 | @scala.inline
19 | def apply(): BuyCake =
20 | val __obj = js.Dynamic.literal()
21 | __obj.updateDynamic("type")(_type.asInstanceOf[js.Any])
22 | __obj.asInstanceOf[BuyCake]
23 |
24 | def unapply(a: Action[String]): Boolean =
25 | a.`type` == _type
26 | end BuyCake
27 | end CakeAction
28 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/basic/CakeContainer.scala:
--------------------------------------------------------------------------------
1 | package demo.basic
2 |
3 | import demo.basic.CakeAction.{BuyCake, CakeAction}
4 | import demo.basic.CakeReducer.State
5 | import japgolly.scalajs.react.component.ScalaFn.Component
6 | import japgolly.scalajs.react.vdom.html_<^.*
7 | import japgolly.scalajs.react.{Callback, CtorType, ScalaFnComponent}
8 | import typings.redux.mod.Dispatch
9 |
10 | import scala.scalajs.js
11 |
12 | // https://www.youtube.com/watch?v=gFZiQnM3Is4
13 | object CakeContainer:
14 | @js.native
15 | trait Props extends js.Object:
16 | val state: State
17 | val dispatch: Dispatch[CakeAction]
18 |
19 | val component: Component[Props, CtorType.Props] = ScalaFnComponent[Props] { props =>
20 | <.div(
21 | <.h2(s"Number of cakes ${props.state.numOfCakes}"),
22 | <.button(^.onClick --> Callback(props.dispatch(BuyCake())))("Buy Cake")
23 | )
24 | }
25 | end CakeContainer
26 |
--------------------------------------------------------------------------------
/react-redux/src/main/scala/demo/basic/CakeReducer.scala:
--------------------------------------------------------------------------------
1 | package demo.basic
2 |
3 | import demo.basic.CakeAction.CakeAction
4 | import typings.redux.mod.Reducer
5 |
6 | import scala.scalajs.js
7 |
8 | object CakeReducer:
9 |
10 | class State(val numOfCakes: Int) extends js.Object
11 |
12 | object State:
13 |
14 | val initial: State = State(10)
15 |
16 | def apply(_numOfCakes: Int): State =
17 | new State(_numOfCakes)
18 |
19 | val Reducer: Reducer[State, CakeAction] = (stateOpt, action) =>
20 | val state = stateOpt.getOrElse(State.initial)
21 | action match
22 | case CakeAction.BuyCake() => State(state.numOfCakes - 1)
23 | case _ => state
24 | end CakeReducer
25 |
--------------------------------------------------------------------------------
/react-router-dom/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-router-dom-slinky demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/react-router-dom/src/main/scala/demo/App.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import org.scalajs.dom
6 | import typings.reactRouter.mod.{`match`, RouteProps}
7 | import typings.reactRouterDom.components.*
8 |
9 | import scala.scalajs.js
10 |
11 | def home: VdomElement = <.div(<.h2("Home"))
12 |
13 | def about: VdomElement = <.div(<.h2("About"))
14 |
15 | val App = ScalaFnComponent[Unit] { _ =>
16 | val renderIntro = <.div(
17 | <.header(^.className := "App-header")(<.h1(^.className := "App-title")("Welcome to React (with Scala.js!)")),
18 | <.p(^.className := "App-intro")("To get started, edit ", <.code("App.scala"), " and save to reload.")
19 | )
20 |
21 | <.div(^.className := "App")(
22 | renderIntro,
23 | BrowserRouter(
24 | <.div(
25 | <.ul(
26 | <.li(Link[js.Object](to = "/")("Home")),
27 | <.li(Link[js.Object](to = "/about")("About")),
28 | <.li(Link[js.Object](to = "/topics")("Topics"))
29 | ),
30 | <.hr(),
31 | Route(RouteProps().setExact(true).setPath("/").setRender(_ => home.rawElement)),
32 | Route(RouteProps().setPath("/about").setRender(_ => about.rawElement)),
33 | Route(RouteProps().setPath("/topics").setRender(props => Topics(props.`match`).rawElement))
34 | )
35 | )
36 | )
37 | }
38 |
39 | val Topics = ScalaFnComponent[`match`[?]] { m =>
40 | <.div(
41 | <.h2("Topics"),
42 | <.ul(
43 | <.li(Link[js.Object](to = m.url + "/rendering")("Rendering with React")),
44 | <.li(Link[js.Object](to = m.url + "/components")("Components")),
45 | <.li(Link[js.Object](to = m.url + "/props-v-state")("Props v. State"))
46 | ),
47 | <.hr(),
48 | Route(
49 | RouteProps()
50 | .setPath(m.path + "/:topicId")
51 | .setRender(props => Topic(props.`match`.asInstanceOf[`match`[Param]]).rawElement)
52 | ),
53 | Route(RouteProps().setExact(true).setPath(m.path).setRender(_ => <.h3("Please select a topic").rawElement))
54 | )
55 | }
56 |
57 | @js.native
58 | trait Param extends js.Object:
59 | val topicId: String = js.native
60 |
61 | val Topic = ScalaFnComponent[`match`[Param]] { m =>
62 | <.div(
63 | <.h3("Topic: " + m.params.topicId)
64 | )
65 | }
66 |
67 | @main
68 | def main: Unit =
69 | val container = Option(dom.document.getElementById("root")).getOrElse {
70 | val elem = dom.document.createElement("div")
71 | elem.id = "root"
72 | dom.document.body.appendChild(elem)
73 | elem
74 | }
75 |
76 | App().renderIntoDOM(container)
77 | end main
78 |
--------------------------------------------------------------------------------
/react-slick/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-slick demo
6 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/react-slick/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import org.scalajs.dom.document
6 | import typings.react.mod.useState
7 | import typings.reactSlick.components.ReactSlick
8 |
9 | import scala.scalajs.js
10 |
11 | case class State(selectedIdx: Option[Int])
12 |
13 | val SlickTest =
14 | ScalaFnComponent[js.Array[String]] { images =>
15 | val js.Tuple2(state, setState) = useState(State(None))
16 |
17 | def myOnClick(idx: Int) =
18 | Callback {
19 | println(s"clicked image $idx")
20 | setState(State(Some(idx)))
21 | }
22 |
23 | val renderedImages: js.Array[VdomElement] =
24 | images.zipWithIndex.map { case (source, idx) =>
25 | <.img(^.key := idx, ^.src := source, ^.onClick --> myOnClick(idx))
26 | }
27 |
28 | <.div(
29 | ^.style := js.Dynamic.literal(position = "relative", left = "200px", width = 500, height = 500),
30 | <.label(
31 | ^.style := js.Dynamic.literal(color = "blue"),
32 | s"Selected image index: ${state.selectedIdx.fold("none")(_.toString)}"
33 | ),
34 | ReactSlick
35 | .onInit(Callback.info("slick init"))
36 | .dots(true)
37 | .autoplay(true)
38 | .autoplaySpeed(1000)
39 | .slidesToShow(2)(renderedImages.to(Seq)*)
40 | )
41 | }
42 |
43 | @main
44 | def main: Unit =
45 | SlickTest(
46 | js.Array(
47 | "https://i.pinimg.com/474x/a8/30/69/a8306979f24cbf615e1cc0a635ceb384.jpg",
48 | "https://i.pinimg.com/474x/b0/15/4c/b0154cfc2fe3a664ac8f679df4debf56.jpg",
49 | "https://i.imgur.com/FqeTKrS.jpg",
50 | "https://static.boredpanda.com/blog/wp-content/uploads/2019/11/cat-fluffy-squirrel-tail-bell-7-5dca63b7b11a8__700.jpg",
51 | "https://i.chzbgr.com/full/9428254976/hD3DA6B8F/cat"
52 | )
53 | ).renderIntoDOM(document.body)
54 |
--------------------------------------------------------------------------------
/react-window/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React-window demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/react-window/src/main/scala/demo/Main.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.ScalaFnComponent
4 | import japgolly.scalajs.react.facade.React.StatelessFunctionalComponent
5 | import japgolly.scalajs.react.vdom.html_<^.*
6 | import org.scalajs.dom
7 | import typings.reactVirtualizedAutoSizer.components.*
8 | import typings.reactWindow.components.*
9 | import typings.reactWindow.mod.ListChildComponentProps
10 |
11 | final val Elements: Vector[String] = Vector.tabulate(1000)(i => s"Element($i)")
12 |
13 | final val Row = ScalaFnComponent { (p: ListChildComponentProps) =>
14 | <.div(^.style := p.style, s"Row(index=${p.index}, data=${Elements(p.index.toInt)})")
15 | }.toJsComponent
16 |
17 | // see https://react-window.now.sh/#/examples/list/fixed-size
18 | @main
19 | def main: Unit =
20 | val rawRow: StatelessFunctionalComponent[ListChildComponentProps] = Row.raw
21 | val rawRowCasted = rawRow.asInstanceOf[typings.react.mod.FunctionComponent[ListChildComponentProps]]
22 |
23 | FixedSizeList(
24 | height = 800,
25 | width = 800,
26 | itemSize = 100,
27 | itemCount = Elements.size,
28 | children = rawRowCasted
29 | ).renderIntoDOM(dom.document.getElementById("container"))
30 | end main
31 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Demos for ScalablyTyped with Scalajs-React flavour
2 |
3 | This is a collection of tiny demo projects to show off how we can use react libraries with Scalajs-react with typings generated from ScalablyTyped
4 |
5 | ## Browser demos
6 |
7 | ### react-mobx
8 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-mobx/)
9 |
10 | `sbt> react-mobx/start` starts a webpack-dev-server at http://localhost:8001 .
11 |
12 | ### react-slick
13 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-slick/)
14 |
15 | `sbt> react-slick/start` starts a webpack-dev-server at http://localhost:8002 .
16 |
17 |
18 | ### react-big-calendar
19 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-big-calendar/)
20 |
21 | `sbt> react-big-calendar/start` starts a webpack-dev-server at http://localhost:8003 .
22 |
23 |
24 | ### semantic-ui-react-kitchensink
25 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/semantic-ui-react-kitchensink/)
26 |
27 | `sbt> semantic-ui-react-kitchensink/start` starts a webpack-dev-server at http://localhost:8004 .
28 |
29 |
30 | ### antd
31 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/antd/)
32 |
33 | `sbt> antd/start` starts a webpack-dev-server at http://localhost:8006 .
34 |
35 |
36 | ### react-router-dom
37 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-router-dom/)
38 |
39 | `sbt> react-router-dom/start` starts a webpack-dev-server at http://localhost:8007 .
40 |
41 |
42 | ### material-ui
43 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/material-ui/)
44 |
45 | `sbt> material-ui/start` starts a webpack-dev-server at http://localhost:8008 .
46 |
47 |
48 | ### react-leaflet
49 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-leaflet/)
50 |
51 | `sbt> react-leaflet/start` starts a webpack-dev-server at http://localhost:8009 .
52 |
53 |
54 | ### office-ui-fabric-react
55 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/office-ui-fabric-react/)
56 |
57 | `sbt> office-ui-fabric-react/start` starts a webpack-dev-server at http://localhost:8010 .
58 |
59 |
60 | ### react-dnd
61 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-dnd/)
62 |
63 | `sbt> react-dnd/start` starts a webpack-dev-server at http://localhost:8011 .
64 |
65 | ### react-i18next
66 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-i18n/)
67 |
68 | `sbt> react-i18n/start` starts a webpack-dev-server at http://localhost:8012 .
69 |
70 | ### @nivo/line
71 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/nivo/)
72 |
73 | `sbt> nivo/start` starts a webpack-dev-server at http://localhost:8013 .
74 |
75 | ### downshift
76 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/downshift/)
77 |
78 | `sbt> downshift/start` starts a webpack-dev-server at http://localhost:8014 .
79 |
80 | ### react-redux
81 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-redux/)
82 |
83 | `sbt> react-redux/start` starts a webpack-dev-server at http://localhost:8015 .
84 |
85 | ### react-window
86 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-window/)
87 |
88 | `sbt> react-window/start` starts a webpack-dev-server at http://localhost:8016 .
89 |
90 | ### react-markdown
91 | [Demo](https://scalablytyped.github.io/ScalaJsReactDemos/react-markdown/)
92 |
93 | `sbt> react-markdown/start` starts a webpack-dev-server at http://localhost:8017 .
94 |
95 | ## React-native
96 | [Expo demo you can run on your phone](https://expo.io/@scalablytyped/scalably-typed-react-native) (slinky version is deployed there)
97 |
98 | To run this you'll need to follow the
99 | [Setting up the development environment](https://reactnative.dev/docs/environment-setup)
100 | for react-native.
101 |
102 | When you have an emulator running, you can start the demo like this:
103 |
104 | ```
105 | sbt>react-native/run
106 | ```
107 |
--------------------------------------------------------------------------------
/semantic-ui-react-kitchensink/readme.md:
--------------------------------------------------------------------------------
1 | ## Kitchen sink demo for semantic-ui-react
2 |
3 | This is a kitchen sink, a loosely organized collection of usages of [Semantic UI React](https://react.semantic-ui.com/), the official Semantic-UI-React integration.
4 |
5 | ### Contribute
6 |
7 | If you have successfully coded other components or subtle features than those shown here, consider adding them for the benefit of the community.
8 |
--------------------------------------------------------------------------------
/semantic-ui-react-kitchensink/src/main/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Semantic-ui-react demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/semantic-ui-react-kitchensink/src/main/scala/demo/App.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.vdom.html_<^.*
4 | import japgolly.scalajs.react.{Callback, ScalaFnComponent}
5 | import org.scalajs.dom
6 | import typings.react.mod.useState
7 | import typings.react.reactStrings.submit
8 | import typings.semanticUiReact.components as Sui
9 | import typings.semanticUiReact.distCommonjsGenericMod.{SemanticICONS, SemanticSIZES, SemanticWIDTHSSTRING}
10 | import typings.semanticUiReact.semanticUiReactStrings.left
11 |
12 | import scala.language.implicitConversions
13 | import scala.scalajs.js
14 |
15 | val App = ScalaFnComponent { case () =>
16 | val js.Tuple2(isModalVisible, updateIsModalVisible) = useState(false)
17 |
18 | <.div(
19 | Sui.Grid(
20 | Sui.GridColumn.width(SemanticWIDTHSSTRING.`1`),
21 | Sui.GridColumn.width(SemanticWIDTHSSTRING.`14`)(
22 | Sui.Divider.horizontal(true)(
23 | Sui.Header.as("h4")(Sui.Icon.name(SemanticICONS.tag), "Button and Icon")
24 | ),
25 | <.p(Sui.Button.primary(true)("Primary")),
26 | <.p(Sui.Icon.name(SemanticICONS.recycle)),
27 | <.p(
28 | Sui.Button.icon(true)(
29 | Sui.Icon.name(SemanticICONS.recycle)
30 | )
31 | ),
32 | <.p(
33 | Sui.Button
34 | .labelPosition(left)
35 | .icon(true)(
36 | Sui.Icon.name(SemanticICONS.pause),
37 | "Pause"
38 | )
39 | ),
40 | Sui.Divider.horizontal(true)(
41 | Sui.Header.as("h4")(
42 | Sui.Icon.name(SemanticICONS.tag),
43 | "Form and Checkbox"
44 | )
45 | ),
46 | Sui.Form(
47 | Sui.FormField(
48 | <.label("First Name"),
49 | <.input(^.placeholder := "First Name")
50 | ),
51 | Sui.FormField(
52 | <.label("Last Name"),
53 | <.input(^.placeholder := "Last Name")
54 | ),
55 | Sui.FormField(
56 | Sui.Checkbox.label("I agree to the Terms and Conditions")
57 | ),
58 | Sui.FormField(Sui.Checkbox.label("I agree to the Cookie Policy").toggle(true)),
59 | Sui.Button.`type`(submit)("OK!")
60 | ),
61 | Sui.Divider.horizontal(true)(
62 | Sui.Header.as("h4")(Sui.Icon.name(SemanticICONS.tag), "Card and Image")
63 | ),
64 | Sui.Card(
65 | Sui.Image
66 | .size(SemanticSIZES.medium)
67 | .wrapped(true)
68 | .ui(false)
69 | .set("src", "https://react.semantic-ui.com/images/avatar/large/matthew.png"),
70 | Sui.CardContent(
71 | Sui.CardHeader("Matthew"),
72 | Sui.CardMeta(<.span(^.className := "date")("Joined in 2015")),
73 | Sui.CardDescription("Matthew is a musician living in Nashville.")
74 | ),
75 | Sui.CardContent.extra(true)(
76 | <.a(Sui.Icon.name(SemanticICONS.user), "22 Friends")
77 | )
78 | ),
79 | Sui.Divider.horizontal(true)(
80 | Sui.Header.as("h4")(Sui.Icon.name(SemanticICONS.tag), "Modal")
81 | ),
82 | <.p(Sui.Button.primary(true).onClick((_, _) => Callback(updateIsModalVisible(true)))("Show modal"))
83 | ),
84 | Sui.GridColumn.width(SemanticWIDTHSSTRING.`1`)
85 | ),
86 | Sui.Modal
87 | .onClose((_, _) => Callback(updateIsModalVisible(false)))
88 | .open(isModalVisible)(
89 | Sui.ModalHeader("Select a Photo"),
90 | Sui.ModalContent.image(true)(
91 | Sui.Image
92 | .size(SemanticSIZES.medium)
93 | .fluid(true)
94 | .wrapped(true)
95 | .set("src", "https://react.semantic-ui.com/images/avatar/large/rachel.png"),
96 | Sui.ModalDescription(
97 | Sui.Header("Default Profile Image"),
98 | <.p("We've found the following gravatar image associated with your e-mail address."),
99 | <.p("Is it okay to use this photo?")
100 | )
101 | )
102 | )
103 | )
104 | }
105 |
106 | @main
107 | def main: Unit =
108 | App(()).renderIntoDOM(dom.document.getElementById("container"))
109 |
--------------------------------------------------------------------------------
/storybook-react/.storybook-dist/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/react';
2 |
3 | function loadStories() {
4 | require('../target/scala-3.1.0/storybook-react-opt.js');
5 | }
6 |
7 | configure(loadStories, module);
8 |
--------------------------------------------------------------------------------
/storybook-react/.storybook-dist/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../.storybook/webpack.config.js');
2 |
--------------------------------------------------------------------------------
/storybook-react/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/react';
2 |
3 | function loadStories() {
4 | require('../target/scala-3.1.0/storybook-react-fastopt.js');
5 | }
6 |
7 | configure(loadStories, module);
8 |
--------------------------------------------------------------------------------
/storybook-react/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * I have no idea.
3 | * Stolen from https://github.com/psychobolt/react-rollup-boilerplate/blob/d9cb9179cb7c00baab486646c504110bf7e2f50a/.storybook/webpack.config.js#L11
4 | */
5 |
6 | module.exports = ({config}) => ({
7 | ...config,
8 | module: {
9 | ...config.module,
10 | rules: [
11 | // Temp fix for issue: https://github.com/storybooks/storybook/issues/3346
12 | ...config.module.rules.filter(rule => !(
13 | (rule.use && rule.use.length && rule.use.find(({loader}) => loader === 'babel-loader'))
14 | )),
15 | {
16 | test: /\.jsx?$/,
17 | include: require('path').resolve('./'),
18 | exclude: /(node_modules|dist)/,
19 | loader: 'babel-loader',
20 | },
21 | {
22 | test: /\.jsx?$/,
23 | exclude: /node_modules/,
24 | loader: 'source-map-loader',
25 | enforce: 'pre',
26 | },
27 | ]
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/storybook-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "storybook": "start-storybook -p 8005 -c .storybook",
4 | "dist": "build-storybook -c .storybook-dist -o ../docs/storybook-react"
5 | },
6 | "dependencies": {
7 | "@storybook/react": "5.3.18",
8 | "@types/node": "13.13.2",
9 | "@types/react": "16.9.34",
10 | "react": "16.13.1",
11 | "typescript": "3.8"
12 | },
13 | "devDependencies": {
14 | "@babel/core": "^7.11.0",
15 | "babel-loader": "^8.1.0",
16 | "es6-shim": "^0.35.5",
17 | "react-dom": "^16.13.1",
18 | "source-map-loader": "^1.0.1",
19 | "webpack": "4.43.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/storybook-react/src/main/scala/demo/Demo.scala:
--------------------------------------------------------------------------------
1 | package demo
2 |
3 | import japgolly.scalajs.react.Callback
4 | import japgolly.scalajs.react.vdom.html_<^.*
5 | import typings.node.global.module
6 | import typings.storybookReact.mod.storiesOf
7 |
8 | object Demo:
9 | def main(args: Array[String]): Unit =
10 | storiesOf("Button", module)
11 | .add("with text", ctx => <.button("Hello Button").rawElement)
12 | .add(
13 | "with some emoji",
14 | ctx =>
15 | <.button(
16 | ^.onClick ==> (e => Callback.alert(s"x: ${e.pageX}, y: ${e.pageY}")),
17 | ^.aria.label := "so cool",
18 | ^.role := "img"
19 | )(<.span("😀😎")).rawElement
20 | )
21 | end Demo
22 |
--------------------------------------------------------------------------------