diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..82da0b16 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..be28ae10 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,5 @@ +# activate meaningful git annotations with: +# git config blame.ignoreRevsFile .git-blame-ignore-revs + +# scalafmt +0ad886c35bdf9c8ad2bfb0501070b8b2ce810710 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5ace4600 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/scripts/gpg-setup.sh b/.github/scripts/gpg-setup.sh new file mode 100755 index 00000000..ad58f407 --- /dev/null +++ b/.github/scripts/gpg-setup.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh + +# from https://github.com/coursier/apps/blob/f1d2bf568bf466a98569a85c3f23c5f3a8eb5360/.github/scripts/gpg-setup.sh + +echo $PGP_SECRET | base64 --decode | gpg --import --no-tty --batch --yes + +echo "allow-loopback-pinentry" >>~/.gnupg/gpg-agent.conf +echo "pinentry-mode loopback" >>~/.gnupg/gpg.conf + +gpg-connect-agent reloadagent /bye diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..93b98f62 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: CI +on: + push: + branches: + - master + tags: + - "v*" + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + SCALA_VERSION: ["2.12.19", "2.13.14"] + steps: + - uses: actions/checkout@v4 + - uses: coursier/cache-action@v6 + - uses: coursier/setup-action@v1.3.5 + with: + jvm: 8 + - run: | + sbtn ++$SCALA_VERSION test + sbtn ++$SCALA_VERSION mimaReportBinaryIssues + env: + SCALA_VERSION: ${{ matrix.SCALA_VERSION }} + + publish: + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: coursier/cache-action@v6 + - uses: coursier/setup-action@v1.3.5 + with: + jvm: 8 + - run: .github/scripts/gpg-setup.sh + env: + PGP_SECRET: ${{ secrets.PUBLISH_SECRET_KEY }} + - name: Release + run: sbtn ci-release + env: + PGP_PASSPHRASE: ${{ secrets.PUBLISH_SECRET_KEY_PASSWORD }} + PGP_SECRET: ${{ secrets.PUBLISH_SECRET_KEY }} + SONATYPE_PASSWORD: ${{ secrets.PUBLISH_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.PUBLISH_USER }} diff --git a/.gitignore b/.gitignore index 2f7896d1..aeb2de6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ target/ +.bsp/ +.idea/ +metals.sbt +.vscode/ diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 00000000..26c5bcea --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,27 @@ +version = "3.8.1" + +runner.dialect = scala213 + +preset = defaultWithAlign + +maxColumn = 120 + +assumeStandardLibraryStripMargin = true + +align { + arrowEnumeratorGenerator = true +} + +newlines { + penalizeSingleSelectMultiArgList = true +} + +rewrite { + rules = [Imports, PreferCurlyFors, RedundantParens] + imports.sort = scalastyle +} + +docstrings.wrap=no + +// only format files tracked by git +project.git = true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2579e8c3..00000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: scala -scala: -- 2.11.8 -- 2.12.1 -jdk: -- oraclejdk8 -script: sbt -batch ++${TRAVIS_SCALA_VERSION} test $(if [[ "$TRAVIS_PULL_REQUEST" == - "false" && ${JAVA_HOME} == $(jdk_switcher home oraclejdk8) && "${TRAVIS_BRANCH}" - == "master" ]]; then echo "publish"; fi) -cache: - directories: - - $HOME/.coursier - - $HOME/.ivy2/cache - - $HOME/.sbt -branches: - only: - - master -env: - global: - - secure: kuxKHPyXy1Bem0JkzkPtWgGmEOIknL5gHDD9kHTn1M7bwgGhqu8KT0AIeHo+h6iJZeALXnXfFZaF1YpOSEq/tIS33PLd5iHrcfmM86vXB7ZFLWBtGh3xytQ7TkvMlP8Xu7vJTcXdJw/1zKW7GJW+9AMK8zrpcBPWhnX/S+Bq1YLya1yxbOJ+qXx0ToL7Bn4AKp4Mucu8HiZ1eVPXXn1aGiCGBDBQDA0EzZvjpZeuJCIUjtr+B5KB4pRF+JHXzQJZeT4jh5zivBr79kPt2gbLK9YfQMLv5a5W2cl8GNv81lQCHsKPXHaQSLXoVz1JSgnfXqJYSXQqZx5DKlsXOgKwg5jzJdJehhEFnPL5gpIM9zLgRhamUea+c4CeP/WzuaR7mKTqovkpXpRfBnHcSXIyTqAhRx7DapWLeIsijSty5arHf0sY9fMeTvUgwoQpPq6VMl0Z5iGo/dQewnruqKIbmXW2NeJE4RMF70DDapLUPgtl8xRsGiEdes9pGKqjn7qj5+9I/fiA3M5SBJqckQfFrnVPbqC+lGnziPa9tG1c7cKXT5tbQDqc8G4K0wazH0c6lDHiwtiMmIGNCTn+PlFf7gEdJ6G5Bsk4TlE/8yqWcCtOUragK6E9pxGaEwCiJTHzXq3NnPByLIFBV/Cz9PpAJze1CeJ4qLPsoHniICKN8j4= - - secure: GXYsqWjiG4t85rt+O4oSWHIg20NGvqQ27QjuhfiU+0sIqsbopMqZhwwKdRDebz7DwpJ3yFkxqchAps2qJjuOMPByLpTIFiNXwBvN/HX+qh/+lTq/qurFcepfKtsgJwrVly5IPaf5Er5jPALiongBt8iJ7LcrOGLYF40GTqYza4VdUMYywEhsd2LJ+mfUjTvLbfrKMwfvfk7nxBK8PLOYqPaX75Q85P5Y8CjArzIpTVh7agQzM9UAKd8HlRKRo2tPYrs7GPFczVqR+SZJONXUf/HcaEhTxOaeC6jfKrDldetFxY+gwD6+T5q4g5bnzEsdzTgmIDYpO/FWB+mNPsnNs1lZiBPi0JpPM0ZoJ93CBun2+CDLTLXlvnHHEpen4LK8ZwUg+Qkel5rJ+ncIHwYQxChv5KdjGgTgaLaFgPbZp6V3wsjlexOKhI+TKG+dJ3hYoZTVwzPi7giPkvNScwExX1L94zmJcEUaTqfF+c4tSX/3xYj4wgIuNqA6c+nUOfPNhkLiEmb9obfFiOCrwkY+LwS/dgOpQNXS7CJGnbut6YBQj+ixUxYiaoUiVhg/OInCUgaHJCtBhh8QBV33tGlq705j023VFMPgTjkW5PzW4BlfYsS0rre4uZzSwtgmxmL2Mq2Hr4d+SywqVFIP81+z16tFHwSh/vO7mw5jSE4wxb8= diff --git a/README.md b/README.md index 6191c85f..b63cac2a 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,19 @@ Scala bindings for [plotly.js](https://plot.ly/javascript/) [](https://travis-ci.org/alexarchambault/plotly-scala) [](https://gitter.im/alexarchambault/plotly-scala?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[](https://maven-badges.herokuapp.com/maven-central/org.plotly-scala/core_2.11) -[](http://javadoc-badge.appspot.com/org.plotly-scala/core_2.11) +[](https://maven-badges.herokuapp.com/maven-central/org.plotly-scala/plotly-render_2.13) +[](http://javadoc-badge.appspot.com/org.plotly-scala/plotly-render_2.13) [Demo](https://alexarchambault.github.io/plotly-scala/) *plotly-scala* is a Scala library able to output JSON that can be passed to [plotly.js](https://plot.ly/javascript/). Its classes closely follow the API of plotly.js, so that one can use plotly-scala by following the [documentation](https://plot.ly/javascript/) of plotly.js. These classes can be converted to JSON, that can be fed directly to plotly.js. -It can be used from [jupyter-scala](https://github.com/alexarchambault/jupyter-scala), from scala-js, or from a Scala REPL like [Ammonite](https://github.com/lihaoyi/Ammonite), to plot things straightaway in the browser. +It can be used from [almond](https://github.com/jupyter-scala/jupyter-scala/tree/develop), from scala-js, or from a Scala REPL like [Ammonite](https://github.com/lihaoyi/Ammonite), to plot things straightaway in the browser. It runs demos of the plotly.js documentation during its tests, to ensure that it is fine with all their features. That allows it to reliably cover a wide range of the plotly.js features - namely, all the examples of the supported sections of the plotly.js documentation are guaranteed to be fine. +It is published for both scala 2.12 and 2.13. + ## Table of content 1. [Quick start](#quick-start) @@ -24,18 +26,17 @@ It runs demos of the plotly.js documentation during its tests, to ensure that it ## Quick start -### From jupyter-scala +### From almond -Simply add the `org.plotly-scala::plotly-jupyter-scala:0.3.1` dependency to the notebook, initialize plotly-scala, and use it, like +Add the `org.plotly-scala::plotly-almond:0.8.1` dependency to the notebook. (Latest version: [](https://maven-badges.herokuapp.com/maven-central/org.plotly-scala/plotly-render_2.13)) +Then initialize plotly-scala, and use it, like ```scala -import $ivy.`org.plotly-scala::plotly-jupyter-scala:0.3.1` +import $ivy.`org.plotly-scala::plotly-almond:0.8.1` import plotly._ import plotly.element._ import plotly.layout._ -import plotly.JupyterScala._ - -plotly.JupyterScala.init() +import plotly.Almond._ val (x, y) = Seq( "Banana" -> 10, @@ -46,14 +47,19 @@ val (x, y) = Seq( Bar(x, y).plot() ``` +#### JupyterLab +If you're using [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/), you have to install [jupyterlab-plotly](https://plotly.com/python/getting-started/#jupyterlab-support-python-35) to enable support for rendering Plotly charts: +```bash +jupyter labextension install jupyterlab-plotly +``` + ### From scala-js Add the corresponding dependency to your project, like ```scala -libraryDependencies += "org.plotly-scala" %%% "plotly-render" % "0.3.1" +libraryDependencies += "org.plotly-scala" %%% "plotly-render" % "0.8.1" ``` - -Note that there are no version published for scala 2.10 yet, because of the limitation of case classes to 22 members with it. +(Latest version: [](https://maven-badges.herokuapp.com/maven-central/org.plotly-scala/plotly-render_2.13)) From your code, add some imports for plotly, ```scala @@ -62,24 +68,20 @@ import plotly._, element._, layout._, Plotly._ Then define plots like ```scala -val x = 0.0 to 10.0 by 0.1 +val x = (0 to 100).map(_ * 0.1) val y1 = x.map(d => 2.0 * d + util.Random.nextGaussian()) val y2 = x.map(math.exp) val plot = Seq( - Scatter( - x, y1, name = "Approx twice" - ), - Scatter( - x, y2, name = "Exp" - ) + Scatter(x, y1).withName("Approx twice"), + Scatter(x, y2).withName("Exp") ) ``` and plot them with + ```scala -plot.plot( - title = "Curves" -) +val lay = Layout().withTitle("Curves") +plot.plot("plot", lay) // attaches to div element with id 'plot' ``` @@ -87,7 +89,7 @@ plot.plot( Load the corresponding dependency, and some imports, like ```scala -import $ivy.`org.plotly-scala::plotly-render:0.3.1` +import $ivy.`org.plotly-scala::plotly-render:0.8.1` import plotly._, element._, layout._, Plotly._ ``` @@ -131,14 +133,15 @@ plotly-scala supports the features illustrated in the following sections of the - [Subplots](https://plot.ly/javascript/subplots/), - [Multiple Axes](https://plot.ly/javascript/multiple-axes/), - [Histograms](https://plot.ly/javascript/histograms/), -- [Log Plots](https://plot.ly/javascript/log-plot/). +- [Log Plots](https://plot.ly/javascript/log-plot/), +- [Image](https://plotly.com/javascript/reference/image/). Some of these are illustrated in the [demo](https://alexarchambault.github.io/plotly-scala/) page. ## Adding support for extra plotly.js features The following workflow can be followed to add support for extra sections of the plotly.js documentation: -- find the corresponding directory in the [source](https://github.com/plotly/documentation/tree/gh-pages/_posts/plotly_js) of the plotly.js documentation. These directories can also be found in the sources of plotly-scala, under `plotly-documentation/_posts/plotly_js`, if its repository has been cloned with the `--recursive` option, +- find the corresponding directory in the [source](https://github.com/alexarchambault/plotly-documentation/tree/eae136bb920c7542654a5e13cff04a0de175a08d/) of the plotly.js documentation. These directories can also be found in the sources of plotly-scala, under `plotly-documentation/_posts/plotly_js`, if its repository has been cloned with the `--recursive` option, - enabling testing of the corresponding documentation section examples in the `DocumentationTests` class, around [this line](https://github.com/alexarchambault/plotly-scala/blob/master/tests/src/test/scala/plotly/doc/DocumentationTests.scala#L224), - running the tests with `sbt ~test`, - fixing the possible Javascript typos in the plotly-documentation submodule in the plotly-scala sources, so that the enabled JS snippets run fine with Rhino from the tests, then committing these fixes, either to [https://github.com/alexarchambault/plotly-documentation](`alexarchambault/plotly-documentation`) or [https://github.com/plotly/documentation](`plotly/documentation`), @@ -148,6 +151,6 @@ The following workflow can be followed to add support for extra sections of the Battlefield tested since early 2016 at [Teads.tv](http://teads.tv) -Released under the LGPL v3 license, copyright 2016 Alexandre Archambault. +Released under the LGPL v3 license, copyright 2016-2019 Alexandre Archambault and contributors. Parts based on the original plotly.js API, which is copyright 2016 Plotly, Inc. diff --git a/almond/src/main/scala/plotly/Almond.scala b/almond/src/main/scala/plotly/Almond.scala new file mode 100644 index 00000000..41e7bb95 --- /dev/null +++ b/almond/src/main/scala/plotly/Almond.scala @@ -0,0 +1,317 @@ +package plotly + +import java.lang.{Boolean => JBoolean, Double => JDouble, Integer => JInt} + +import almond.interpreter.api.{DisplayData, OutputHandler} + +import scala.util.Random +import plotly.element._ +import plotly.layout._ + +object Almond { + + object Internal { + @volatile var initialized = false + } + + def init(offline: Boolean = false)(implicit publish: OutputHandler): Unit = { + + // offline mode like in plotly-python + + val requireInit = + if (offline) + s"""define('plotly', function(require, exports, module) { + | ${Plotly.plotlyMinJs} + |}); + """.stripMargin + else + s"""require.config({ + | paths: { + | d3: 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min', + | plotly: 'https://cdn.plot.ly/plotly-${Plotly.plotlyVersion}.min', + | jquery: 'https://code.jquery.com/jquery-3.3.1.min' + | }, + | + | shim: { + | plotly: { + | deps: ['d3', 'jquery'], + | exports: 'plotly' + | } + | } + |}); + """.stripMargin + + val html = s""" + + """ + + Internal.initialized = true + + publish.html(html) + } + + def plotJs( + data: Seq[Trace], + layout: Layout, + config: Config, + div: String = "" + )(implicit + publish: OutputHandler + ): String = { + + val (div0, divPart) = + if (div.isEmpty) { + val d = randomDiv() + (d, s"""
""") + } else + (div, "") + + val baseJs = Plotly.jsSnippet(div0, data, layout, config) + val json = Plotly.jsonSnippet(data, layout, config) + + val js = + s"""require(['plotly'], function(Plotly) { + | $baseJs + |}); + """.stripMargin + + val data0 = DisplayData( + data = Map( + "text/html" -> + s"""$divPart + | + """.stripMargin, + "application/vnd.plotly.v1+json" -> json + ) + ) + + publish.display(data0) + + div0 + } + + def randomDiv(): String = + almond.display.UpdatableDisplay.generateDiv("plot-") + + def plot( + data: Seq[Trace], + layout: Layout = Layout(), + config: Config = Config(), + div: String = "" + )(implicit + publish: OutputHandler + ): String = { + + if (!Internal.initialized) + Internal.synchronized { + if (!Internal.initialized) { + init() + Internal.initialized = true + } + } + + plotJs(data, layout, config) + } + + implicit class DataOps(val data: Trace) extends AnyVal { + + @deprecated("Create a Layout and / or a Config, and call one of the other plot methods instead", "0.8.0") + def plot( + title: String = null, + legend: Legend = null, + width: JInt = null, + height: JInt = null, + showlegend: JBoolean = null, + xaxis: Axis = null, + yaxis: Axis = null, + xaxis1: Axis = null, + xaxis2: Axis = null, + xaxis3: Axis = null, + xaxis4: Axis = null, + yaxis1: Axis = null, + yaxis2: Axis = null, + yaxis3: Axis = null, + yaxis4: Axis = null, + barmode: BarMode = null, + autosize: JBoolean = null, + margin: Margin = null, + annotations: Seq[Annotation] = null, + plot_bgcolor: Color = null, + paper_bgcolor: Color = null, + font: Font = null, + bargap: JDouble = null, + bargroupgap: JDouble = null, + hovermode: HoverMode = null, + boxmode: BoxMode = null, + editable: JBoolean = null, + responsive: JBoolean = null, + showEditInChartStudio: JBoolean = null, + plotlyServerURL: String = null, + div: String = "" + )(implicit + publish: OutputHandler + ): String = + plot( + Layout( + title, + legend, + width, + height, + showlegend, + xaxis, + yaxis, + xaxis1, + xaxis2, + xaxis3, + xaxis4, + yaxis1, + yaxis2, + yaxis3, + yaxis4, + barmode, + autosize, + margin, + annotations, + plot_bgcolor, + paper_bgcolor, + font, + bargap, + bargroupgap, + hovermode, + boxmode + ), + Config() + .withEditable(Option(editable).map[Boolean](identity)) + .withResponsive(Option(responsive).map[Boolean](identity)) + .withShowEditInChartStudio(Option(showEditInChartStudio).map[Boolean](identity)) + .withPlotlyServerURL(Option(plotlyServerURL)), + div + ) + + def plot( + layout: Layout, + config: Config, + div: String + )(implicit + publish: OutputHandler + ): String = + Almond.plot(Seq(data), layout, config, div = div) + + def plot()(implicit + publish: OutputHandler + ): String = + plot(Layout(), Config(), "") + + def plot( + layout: Layout + )(implicit + publish: OutputHandler + ): String = + plot(layout, Config(), "") + + def plot( + config: Config + )(implicit + publish: OutputHandler + ): String = + plot(Layout(), config, "") + + def plot( + layout: Layout, + config: Config + )(implicit + publish: OutputHandler + ): String = + plot(layout, config, "") + } + + implicit class DataSeqOps(val data: Seq[Trace]) extends AnyVal { + def plot( + title: String = null, + legend: Legend = null, + width: JInt = null, + height: JInt = null, + showlegend: JBoolean = null, + xaxis: Axis = null, + yaxis: Axis = null, + xaxis1: Axis = null, + xaxis2: Axis = null, + xaxis3: Axis = null, + xaxis4: Axis = null, + yaxis1: Axis = null, + yaxis2: Axis = null, + yaxis3: Axis = null, + yaxis4: Axis = null, + barmode: BarMode = null, + autosize: JBoolean = null, + margin: Margin = null, + annotations: Seq[Annotation] = null, + plot_bgcolor: Color = null, + paper_bgcolor: Color = null, + font: Font = null, + bargap: JDouble = null, + bargroupgap: JDouble = null, + hovermode: HoverMode = null, + boxmode: BoxMode = null, + editable: JBoolean = null, + responsive: JBoolean = null, + showEditInChartStudio: JBoolean = null, + plotlyServerURL: String = null, + div: String = "" + )(implicit + publish: OutputHandler + ): String = + plot( + Layout( + title, + legend, + width, + height, + showlegend, + xaxis, + yaxis, + xaxis1, + xaxis2, + xaxis3, + xaxis4, + yaxis1, + yaxis2, + yaxis3, + yaxis4, + barmode, + autosize, + margin, + annotations, + plot_bgcolor, + paper_bgcolor, + font, + bargap, + bargroupgap, + hovermode, + boxmode + ), + Config() + .withEditable(Option(editable).map[Boolean](identity)) + .withResponsive(Option(responsive).map[Boolean](identity)) + .withShowEditInChartStudio(Option(showEditInChartStudio).map[Boolean](identity)) + .withPlotlyServerURL(plotlyServerURL), + div + ) + + def plot( + layout: Layout, + config: Config, + div: String + )(implicit + publish: OutputHandler + ): String = + Almond.plot(data, layout, config, div = div) + } + +} diff --git a/build.sbt b/build.sbt index 503281d3..e5e34c5c 100644 --- a/build.sbt +++ b/build.sbt @@ -1,72 +1,142 @@ +import java.nio.charset.StandardCharsets +import java.nio.file.Files -import Aliases._ import Settings._ +import sbtcrossproject.CrossPlugin.autoImport.crossProject -lazy val core = crossProject +inThisBuild( + List( + organization := "org.plotly-scala", + homepage := Some(url("https://github.com/alexarchambault/plotly-scala")), + licenses := Seq("LGPL 3.0" -> url("http://opensource.org/licenses/LGPL-3.0")), + developers := List( + Developer( + "alexarchambault", + "Alexandre Archambault", + "", + url("https://github.com/alexarchambault") + ) + ) + ) +) + +val previousVersions = Set.empty[String] +lazy val mimaSettings = Def.settings( + mimaPreviousArtifacts := previousVersions.map(organization.value %% moduleName.value % _) +) + +lazy val core = crossProject(JVMPlatform, JSPlatform) + .jsConfigure(_.disablePlugins(MimaPlugin)) .settings( shared, - plotlyPrefix + plotlyPrefix, + libraryDependencies += Deps.dataClass % Provided + ) + .jvmSettings( + mimaSettings ) lazy val coreJvm = core.jvm -lazy val coreJs = core.js +lazy val coreJs = core.js lazy val `joda-time` = project .dependsOn(coreJvm) .settings( shared, + mimaSettings, plotlyPrefix, - libs += Deps.jodaTime + libraryDependencies += Deps.jodaTime ) -lazy val `circe-simple-generic` = crossProject - .settings( - shared, - libs ++= Seq( - Deps.circeCore.value, - Deps.circeParser.value, - Deps.shapeless.value, - Deps.scalacheckShapeless.value % "test" - ), - utest - ) - .jsSettings( - scalaJSStage in Global := FastOptStage - ) - -lazy val circeSimpleGenericJvm = `circe-simple-generic`.jvm -lazy val circeSimpleGenericJs = `circe-simple-generic`.js - -lazy val render = crossProject - .dependsOn(core, `circe-simple-generic`) +lazy val render = crossProject(JVMPlatform, JSPlatform) + .jvmConfigure(_.enablePlugins(ShadingPlugin)) + .jsConfigure(_.disablePlugins(MimaPlugin)) + .dependsOn(core) .settings( shared, plotlyPrefix ) .jvmSettings( - libs += WebDeps.plotlyJs + mimaSettings, + mimaCurrentClassfiles := shadedPackageBin.value, + Mima.renderFilters, + shadedModules += Deps.argonautShapeless.value.module, + shadingRules ++= { + val shadeUnder = "plotly.internals.shaded" + val shadeNamespaces = Seq("argonaut", "macrocompat", "shapeless") + for (ns <- shadeNamespaces) + yield ShadingRule.moveUnder(ns, shadeUnder), + }, + validNamespaces += "plotly", + libraryDependencies ++= Seq( + Deps.argonautShapeless.value, + // depending on that one so that it doesn't get shaded + "org.scala-lang" % "scala-reflect" % scalaVersion.value, + WebDeps.plotlyJs, + Deps.scalaTest % "test" + ), + (Compile / resourceGenerators) += Def.task { + import sys.process._ + + val log = state.value.log + + val dir = (Compile / classDirectory).value / "plotly" + val ver = version.value + + val f = dir / "plotly-scala.properties" + dir.mkdirs() + + val props = Seq( + "plotly-js-version" -> WebDeps.Versions.plotlyJs, + "version" -> ver, + "commit-hash" -> Seq("git", "rev-parse", "HEAD").!!.trim + ) + + val b = props + .map { case (k, v) => + assert(!v.contains("\n"), s"Invalid ${"\\n"} character in property $k") + s"$k=$v" + } + .mkString("\n") + .getBytes(StandardCharsets.UTF_8) + + val currentContentOpt = Some(f.toPath) + .filter(Files.exists(_)) + .map(p => Files.readAllBytes(p)) + + if (currentContentOpt.forall(b0 => !java.util.Arrays.equals(b, b0))) { + val w = new java.io.FileOutputStream(f) + w.write(b) + w.close() + + log.info(s"Wrote $f") + } + + Nil + } ) .jsSettings( - libs ++= Seq( - Deps.circeScalaJs.value, + libraryDependencies ++= Seq( + Deps.argonautShapeless.value, Deps.scalajsDom.value ) ) lazy val renderJvm = render.jvm -lazy val renderJs = render.js +lazy val renderJs = render.js lazy val demo = project - .enablePlugins(ScalaJSPlugin) + .enablePlugins(JSDependenciesPlugin, ScalaJSPlugin) + .disablePlugins(MimaPlugin) .dependsOn(renderJs) .settings( shared, - dontPublish, + (publish / skip) := true, plotlyPrefix, - test in Test := (), - testOnly in Test := (), - libs += Deps.scalatags.value, + (Test / test) := {}, + (Test / testOnly) := {}, + libraryDependencies += Deps.scalatags.value, jsDependencies ++= Seq( WebDeps.plotlyJs .intransitive() @@ -105,44 +175,28 @@ lazy val demo = project ) lazy val tests = project + .disablePlugins(MimaPlugin) .dependsOn(coreJvm, renderJvm) .settings( shared, - dontPublish, + (publish / skip) := true, plotlyPrefix, - libs ++= Seq( - Deps.circeLiteral.value % "test", + fetchTestData, + libraryDependencies ++= Seq( Deps.scalaTest % "test", - Deps.rhino % "test" + Deps.rhino % "test" ) ) -lazy val `jupyter-scala` = project +lazy val almond = project .dependsOn(coreJvm, renderJvm) .settings( shared, + mimaSettings, plotlyPrefix, - libs ++= Seq( - Deps.jupyterScalaApi % "provided" - ) + libraryDependencies += Deps.almondScalaApi % "provided" ) - -lazy val `plotly-scala` = project - .in(file(".")) - .aggregate( - coreJvm, - coreJs, - `joda-time`, - circeSimpleGenericJvm, - circeSimpleGenericJs, - renderJvm, - renderJs, - demo, - tests, - `jupyter-scala` - ) - .settings( - shared, - dontPublish - ) +crossScalaVersions := Nil +(publish / skip) := true +disablePlugins(MimaPlugin) diff --git a/circe-simple-generic/README.md b/circe-simple-generic/README.md deleted file mode 100644 index 03ea17e1..00000000 --- a/circe-simple-generic/README.md +++ /dev/null @@ -1 +0,0 @@ -Simple port of [argonaut-shapeless](https://github.com/alexarchambault/argonaut-shapeless) to circe diff --git a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/DerivedInstances.scala b/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/DerivedInstances.scala deleted file mode 100644 index 2077bdc1..00000000 --- a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/DerivedInstances.scala +++ /dev/null @@ -1,21 +0,0 @@ -package io.circe.simplegeneric.derive - -import shapeless.{ LowPriority, Strict } -import io.circe.{ Decoder, Encoder } - -trait DerivedInstances { - - implicit def derivedEncoder[T] - (implicit - ev: LowPriority, - underlying: Strict[MkEncoder[T]] - ): Encoder[T] = - underlying.value.encoder - - implicit def derivedDecoder[T] - (implicit - ev: LowPriority, - underlying: Strict[MkDecoder[T]] - ): Decoder[T] = - underlying.value.decoder -} diff --git a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/JsonProductCodec.scala b/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/JsonProductCodec.scala deleted file mode 100644 index 5b3ab782..00000000 --- a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/JsonProductCodec.scala +++ /dev/null @@ -1,62 +0,0 @@ -package io.circe.simplegeneric -package derive - -import io.circe.{ ACursor, Decoder, HCursor, Json } - -abstract class JsonProductCodec { - def encodeEmpty: Json - def encodeField(field: (String, Json), obj: Json, default: => Option[Json]): Json - - def decodeEmpty(cursor: HCursor): Decoder.Result[Unit] - def decodeField[A](name: String, cursor: HCursor, decode: Decoder[A], default: Option[A]): Decoder.Result[(A, ACursor)] -} - -object JsonProductCodec { - val obj: JsonProductCodec = new JsonProductObjCodec - def adapt(f: String => String): JsonProductCodec = new JsonProductObjCodec { - override def toJsonName(name: String) = f(name) - } -} - -abstract class JsonProductCodecFor[P] { - def codec: JsonProductCodec -} - -object JsonProductCodecFor { - def apply[S](codec0: JsonProductCodec): JsonProductCodecFor[S] = - new JsonProductCodecFor[S] { - def codec = codec0 - } - - implicit def default[T]: JsonProductCodecFor[T] = - JsonProductCodecFor(JsonProductCodec.obj) -} - -class JsonProductObjCodec extends JsonProductCodec { - - def toJsonName(name: String): String = name - - val encodeEmpty: Json = Json.obj() - def encodeField(field: (String, Json), obj: Json, default: => Option[Json]): Json = { - val (name, content) = field - if (default.toSeq.contains(content)) - obj - else - obj.mapObject((toJsonName(name) -> content) +: _) - } - - def decodeEmpty(cursor: HCursor): Decoder.Result[Unit] = Right(()) - def decodeField[A](name: String, cursor: HCursor, decode: Decoder[A], default: Option[A]): Decoder.Result[(A, ACursor)] = { - val c = cursor.downField(toJsonName(name)) - def result = c.as(decode).right.map((_, ACursor.ok(cursor))) - - default match { - case None => result - case Some(d) => - if (c.succeeded) - result - else - Right((d, ACursor.ok(cursor))) - } - } -} diff --git a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/JsonSumCodec.scala b/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/JsonSumCodec.scala deleted file mode 100644 index d20ac78c..00000000 --- a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/JsonSumCodec.scala +++ /dev/null @@ -1,93 +0,0 @@ -package io.circe.simplegeneric -package derive - -import io.circe._ - -abstract class JsonSumCodec { - def encodeEmpty: Nothing - def encodeField(fieldOrObj: Either[Json, (String, Json)]): Json - - def decodeEmpty(cursor: HCursor): Decoder.Result[Nothing] - def decodeField[A](name: String, cursor: HCursor, decode: Decoder[A]): Decoder.Result[Either[ACursor, A]] -} - -abstract class JsonSumCodecFor[S] { - def codec: JsonSumCodec -} - -object JsonSumCodecFor { - def apply[S](codec0: JsonSumCodec): JsonSumCodecFor[S] = - new JsonSumCodecFor[S] { - def codec = codec0 - } - - implicit def default[T]: JsonSumCodecFor[T] = - JsonSumCodecFor(JsonSumCodec.obj) -} - -object JsonSumCodec { - val obj: JsonSumCodec = new JsonSumObjCodec - val typeField: JsonSumCodec = new JsonSumTypeFieldCodec -} - -class JsonSumObjCodec extends JsonSumCodec { - - def toJsonName(name: String): String = name - - def encodeEmpty: Nothing = - throw new IllegalArgumentException("empty") - def encodeField(fieldOrObj: Either[Json, (String, Json)]): Json = - fieldOrObj match { - case Left(other) => other - case Right((name, content)) => - Json.obj(toJsonName(name) -> content) - } - - def decodeEmpty(cursor: HCursor): Decoder.Result[Nothing] = - Left(DecodingFailure( - s"unrecognized type(s): ${cursor.fields.getOrElse(Nil).mkString(", ")}", - cursor.history - )) - def decodeField[A](name: String, cursor: HCursor, decode: Decoder[A]): Decoder.Result[Either[ACursor, A]] = - cursor.downField(toJsonName(name)).either match { - case Left(_) => - Right(Left(ACursor.ok(cursor))) - case Right(content) => - decode(content).right.map(Right(_)) - } -} - -class JsonSumTypeFieldCodec extends JsonSumCodec { - - def typeField: String = "type" - - def toTypeValue(name: String) = name - - def encodeEmpty: Nothing = - throw new IllegalArgumentException("empty") - def encodeField(fieldOrObj: Either[Json, (String, Json)]): Json = - fieldOrObj match { - case Left(other) => other - case Right((name, content)) => - content.mapObject((typeField -> Json.fromString(toTypeValue(name))) +: _) - } - - def decodeEmpty(cursor: HCursor): Decoder.Result[Nothing] = - Left(DecodingFailure( - cursor.downField(typeField).focus match { - case None => "no type found" - case Some(type0) => s"unrecognized type: $type0" - }, - cursor.history - )) - def decodeField[A](name: String, cursor: HCursor, decode: Decoder[A]): Decoder.Result[Either[ACursor, A]] = { - val c = cursor.downField(typeField) - - c.as[String] match { - case Right(name0) if toTypeValue(name) == name0 => - c.delete.as(decode).right.map(Right(_)) - case _ => - Right(Left(ACursor.ok(cursor))) - } - } -} diff --git a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/MkDecoder.scala b/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/MkDecoder.scala deleted file mode 100644 index 387e4095..00000000 --- a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/MkDecoder.scala +++ /dev/null @@ -1,182 +0,0 @@ -package io.circe.simplegeneric -package derive - -import shapeless._ -import shapeless.labelled.{FieldType, field} -import io.circe.Decoder - -abstract class MkDecoder[T] { - def decoder: Decoder[T] -} - -object MkDecoder { - def apply[T](implicit decoder: MkDecoder[T]): MkDecoder[T] = decoder - - implicit def product[P] - (implicit - underlying: ProductDecoder[P], - codecFor: JsonProductCodecFor[P] - ): MkDecoder[P] = - new MkDecoder[P] { - def decoder = underlying(codecFor.codec) - } - - implicit def sum[S] - (implicit - underlying: SumDecoder[S], - codecFor: JsonSumCodecFor[S] - ): MkDecoder[S] = - new MkDecoder[S] { - def decoder = underlying(codecFor.codec) - } -} - -abstract class ProductDecoder[P] { - def apply(productCodec: JsonProductCodec): Decoder[P] -} - -object ProductDecoder { - def apply[P](implicit decoder: ProductDecoder[P]): ProductDecoder[P] = decoder - - def instance[P](f: JsonProductCodec => Decoder[P]): ProductDecoder[P] = - new ProductDecoder[P] { - def apply(productCodec: JsonProductCodec) = - f(productCodec) - } - - // Re-enable by making a dummy HList of defaults made of Option[_] - // implicit def record[R <: HList] - // (implicit - // underlying: HListProductDecoder[R] - // ): ProductDecoder[R] = - // instance { productCodec => - // underlying(productCodec) - // } - - implicit def generic[P, L <: HList, D <: HList] - (implicit - gen: LabelledGeneric.Aux[P, L], - defaults: Default.AsOptions.Aux[P, D], - underlying: Lazy[HListProductDecoder[L, D]] - ): ProductDecoder[P] = - instance { productCodec => - underlying.value(productCodec, defaults()) - .map(gen.from) - } -} - -abstract class HListProductDecoder[L <: HList, D <: HList] { - def apply(productCodec: JsonProductCodec, defaults: D): Decoder[L] -} - -object HListProductDecoder { - def apply[L <: HList, D <: HList](implicit decoder: HListProductDecoder[L, D]): HListProductDecoder[L, D] = - decoder - - def instance[L <: HList, D <: HList](f: (JsonProductCodec, D) => Decoder[L]): HListProductDecoder[L, D] = - new HListProductDecoder[L, D] { - def apply(productCodec: JsonProductCodec, defaults: D) = - f(productCodec, defaults) - } - - implicit val hnil: HListProductDecoder[HNil, HNil] = - instance { (productCodec, defaults) => - Decoder.instance { c => - productCodec - .decodeEmpty(c) - .right - .map(_ => HNil) - } - } - - implicit def hcons[K <: Symbol, H, T <: HList, TD <: HList] - (implicit - key: Witness.Aux[K], - headDecode: Strict[Decoder[H]], - tailDecode: HListProductDecoder[T, TD] - ): HListProductDecoder[FieldType[K, H] :: T, Option[H] :: TD] = - instance { (productCodec, defaults) => - lazy val tailDecode0 = tailDecode(productCodec, defaults.tail) - - Decoder.instance { c => - for { - x <- productCodec.decodeField(key.value.name, c, headDecode.value, defaults.head).right - t <- x._2.as(tailDecode0).right - } yield field[K](x._1) :: t - } - } -} - -abstract class CoproductSumDecoder[C <: Coproduct] { - def apply(sumCodec: JsonSumCodec): Decoder[C] -} - -object CoproductSumDecoder { - def apply[C <: Coproduct](implicit decoder: CoproductSumDecoder[C]): CoproductSumDecoder[C] = - decoder - - def instance[C <: Coproduct](f: JsonSumCodec => Decoder[C]): CoproductSumDecoder[C] = - new CoproductSumDecoder[C] { - def apply(sumCodec: JsonSumCodec) = - f(sumCodec) - } - - implicit val cnil: CoproductSumDecoder[CNil] = - instance { sumCodec => - Decoder.instance { c => - sumCodec - .decodeEmpty(c) - .right - .map(t => t: CNil) - } - } - - implicit def ccons[K <: Symbol, H, T <: Coproduct] - (implicit - key: Witness.Aux[K], - headDecode: Lazy[Decoder[H]], - tailDecode: CoproductSumDecoder[T] - ): CoproductSumDecoder[FieldType[K, H] :+: T] = - instance { sumCodec => - lazy val tailDecode0 = tailDecode(sumCodec) - - Decoder.instance { c => - sumCodec.decodeField(key.value.name, c, headDecode.value).right.flatMap { - case Left(tailCursor) => tailCursor.as(tailDecode0).right.map(Inr(_)) - case Right(h) => Right(Inl(field[K](h))) - } - } - } -} - -abstract class SumDecoder[S] { - def apply(sumCodec: JsonSumCodec): Decoder[S] -} - -object SumDecoder { - def apply[S](implicit decoder: SumDecoder[S]): SumDecoder[S] = decoder - - def instance[S](f: JsonSumCodec => Decoder[S]): SumDecoder[S] = - new SumDecoder[S] { - def apply(sumCodec: JsonSumCodec) = - f(sumCodec) - } - - implicit def union[U <: Coproduct] - (implicit - underlying: CoproductSumDecoder[U] - ): SumDecoder[U] = - instance { sumCodec => - underlying(sumCodec) - } - - implicit def generic[S, C <: Coproduct] - (implicit - gen: LabelledGeneric.Aux[S, C], - underlying: Strict[CoproductSumDecoder[C]] - ): SumDecoder[S] = - instance { sumCodec => - underlying.value(sumCodec) - .map(gen.from) - } -} diff --git a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/MkEncoder.scala b/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/MkEncoder.scala deleted file mode 100644 index 0ab101a6..00000000 --- a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/derive/MkEncoder.scala +++ /dev/null @@ -1,181 +0,0 @@ -package io.circe.simplegeneric -package derive - -import io.circe.Encoder -import shapeless._ -import shapeless.labelled.FieldType - -abstract class MkEncoder[T] { - def encoder: Encoder[T] -} - -object MkEncoder { - def apply[T](implicit encoder: MkEncoder[T]): MkEncoder[T] = encoder - - implicit def product[P] - (implicit - underlying: Strict[ProductEncoder[P]], - codecFor: JsonProductCodecFor[P] - ): MkEncoder[P] = - new MkEncoder[P] { - def encoder = underlying.value(codecFor.codec) - } - - implicit def sum[S] - (implicit - underlying: Strict[SumEncoder[S]], - codecFor: JsonSumCodecFor[S] - ): MkEncoder[S] = - new MkEncoder[S] { - def encoder = underlying.value(codecFor.codec) - } -} - -abstract class ProductEncoder[P] { - def apply(productCodec: JsonProductCodec): Encoder[P] -} - -object ProductEncoder { - def apply[P](implicit encoder: ProductEncoder[P]): ProductEncoder[P] = encoder - - def instance[P](f: JsonProductCodec => Encoder[P]): ProductEncoder[P] = - new ProductEncoder[P] { - def apply(productCodec: JsonProductCodec) = - f(productCodec) - } - - // TODO Generate an HList made of Option[...] as to use as default - // implicit def record[R <: HList] - // (implicit - // underlying: HListProductEncoder[R] - // ): ProductEncoder[R] = - // instance { productCodec => - // underlying(productCodec) - // } - - implicit def generic[P, L <: HList, D <: HList] - (implicit - gen: LabelledGeneric.Aux[P, L], - defaults: Default.AsOptions.Aux[P, D], - underlying: Lazy[HListProductEncoder[L, D]] - ): ProductEncoder[P] = - instance { productCodec => - underlying.value(productCodec, defaults()) - .contramap(gen.to) - } -} - -abstract class HListProductEncoder[L <: HList, D <: HList] { - def apply(productCodec: JsonProductCodec, defaults: D): Encoder[L] -} - -object HListProductEncoder { - def apply[L <: HList, D <: HList](implicit encoder: HListProductEncoder[L, D]): HListProductEncoder[L, D] = - encoder - - def instance[L <: HList, D <: HList](f: (JsonProductCodec, D) => Encoder[L]): HListProductEncoder[L, D] = - new HListProductEncoder[L, D] { - def apply(productCodec: JsonProductCodec, defaults: D) = - f(productCodec, defaults) - } - - implicit val hnil: HListProductEncoder[HNil, HNil] = - instance { (productCodec, _) => - Encoder.instance { _ => - productCodec.encodeEmpty - } - } - - implicit def hcons[K <: Symbol, H, T <: HList, TD <: HList] - (implicit - key: Witness.Aux[K], - headEncode: Strict[Encoder[H]], - tailEncode: HListProductEncoder[T, TD] - ): HListProductEncoder[FieldType[K, H] :: T, Option[H] :: TD] = - instance { (productCodec, defaults) => - lazy val defaultOpt = defaults.head.map(headEncode.value(_)) - lazy val tailEncode0 = tailEncode(productCodec, defaults.tail) - - Encoder.instance { l => - productCodec.encodeField( - key.value.name -> headEncode.value(l.head), - tailEncode0(l.tail), - defaultOpt - ) - } - } -} - - -abstract class SumEncoder[S] { - def apply(sumCodec: JsonSumCodec): Encoder[S] -} - -object SumEncoder { - def apply[S](implicit encoder: SumEncoder[S]): SumEncoder[S] = encoder - - def instance[S](f: JsonSumCodec => Encoder[S]): SumEncoder[S] = - new SumEncoder[S] { - def apply(sumCodec: JsonSumCodec) = - f(sumCodec) - } - - implicit def union[U <: Coproduct] - (implicit - underlying: CoproductSumEncoder[U] - ): SumEncoder[U] = - instance { sumCodec => - underlying(sumCodec) - } - - implicit def generic[S, C <: Coproduct] - (implicit - gen: LabelledGeneric.Aux[S, C], - underlying: Strict[CoproductSumEncoder[C]] - ): SumEncoder[S] = - instance { sumCodec => - underlying.value(sumCodec) - .contramap(gen.to) - } -} - -abstract class CoproductSumEncoder[C <: Coproduct] { - def apply(sumCodec: JsonSumCodec): Encoder[C] -} - -object CoproductSumEncoder { - def apply[C <: Coproduct](implicit encoder: CoproductSumEncoder[C]): CoproductSumEncoder[C] = - encoder - - def instance[C <: Coproduct](f: JsonSumCodec => Encoder[C]): CoproductSumEncoder[C] = - new CoproductSumEncoder[C] { - def apply(sumCodec: JsonSumCodec) = - f(sumCodec) - } - - implicit val cnil: CoproductSumEncoder[CNil] = - instance { sumCodec => - Encoder.instance { c => - sumCodec.encodeEmpty - } - } - - implicit def ccons[K <: Symbol, H, T <: Coproduct] - (implicit - key: Witness.Aux[K], - headEncode: Lazy[Encoder[H]], - tailEncode: CoproductSumEncoder[T] - ): CoproductSumEncoder[FieldType[K, H] :+: T] = - instance { sumCodec => - lazy val tailEncode0 = tailEncode(sumCodec) - - Encoder.instance { - case Inl(h) => - sumCodec.encodeField( - Right(key.value.name -> headEncode.value(h)) - ) - case Inr(r) => - tailEncode0(r) - } - } -} diff --git a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/package.scala b/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/package.scala deleted file mode 100644 index fe53cde4..00000000 --- a/circe-simple-generic/shared/src/main/scala/io/circe/simplegeneric/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package io.circe - -import io.circe.simplegeneric.derive.DerivedInstances - -package object simplegeneric extends DerivedInstances diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/DefaultTests.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/DefaultTests.scala deleted file mode 100644 index 2e53803d..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/DefaultTests.scala +++ /dev/null @@ -1,45 +0,0 @@ -package io.circe.simplegeneric - -import io.circe.{ Decoder, Encoder, Json } -import utest._ - -object DefaultTests extends TestSuite { - - case class WithDefaults( - i: Int, - s: String = "b" - ) - - val tests = TestSuite { - 'simple - { - val encoder = Encoder[WithDefaults] - val decoder = Decoder[WithDefaults] - - val value0 = WithDefaults(2, "a") - val json0 = encoder(value0) - val expectedJson0 = Json.obj( - "i" -> Json.fromInt(2), - "s" -> Json.fromString("a") - ) - - assert(json0 == expectedJson0) - - val value1 = WithDefaults(2) - val json1 = encoder(value1) - val expectedJson1 = Json.obj( - "i" -> Json.fromInt(2) - ) - - assert(json1 == expectedJson1) - - val result0 = decoder.decodeJson(expectedJson0) - val expectedResult0 = Right(value0) - assert(result0 == expectedResult0) - - val result1 = decoder.decodeJson(expectedJson1) - val expectedResult1 = Right(value1) - assert(result1 == expectedResult1) - } - } - -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/Definitions.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/Definitions.scala deleted file mode 100644 index d8fa4c46..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/Definitions.scala +++ /dev/null @@ -1,38 +0,0 @@ -package io.circe.simplegeneric - -import io.circe.Json - -// These case classes / ADTs were originally the same as in scalacheck-shapeless - -/* - * We should have codecs for these - */ -case object Empty -case class EmptyCC() -case class Simple(i: Int, s: String, blah: Boolean) -case class Composed(foo: Simple, other: String) -case class TwiceComposed(foo: Simple, bar: Composed, v: Int) -case class ComposedOptList(fooOpt: Option[Simple], other: String, l: List[TwiceComposed]) - -case class OI(oi: Option[Int]) - -case class SimpleWithJs(i: Int, s: String, v: Json) - -case class NowThree(s: String, i: Int, n: Double) - -sealed trait Base -case class BaseIS(i: Int, s: String) extends Base -case class BaseDB(d: Double, b: Boolean) extends Base -case class BaseLast(c: Simple) extends Base - -/* - * We should *not* have codecs for these - */ -trait NoArbitraryType -case class ShouldHaveNoArb(n: NoArbitraryType, i: Int) -case class ShouldHaveNoArbEither(s: String, i: Int, n: NoArbitraryType) - -sealed trait BaseNoArb -case class BaseNoArbIS(i: Int, s: String) extends BaseNoArb -case class BaseNoArbDB(d: Double, b: Boolean) extends BaseNoArb -case class BaseNoArbN(n: NoArbitraryType) extends BaseNoArb diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/LowPriorityTests.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/LowPriorityTests.scala deleted file mode 100644 index 2b97f396..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/LowPriorityTests.scala +++ /dev/null @@ -1,153 +0,0 @@ -package io.circe.simplegeneric - -import shapeless._ - -import utest._ - -object LowPriorityTests extends TestSuite { - - object Simple { - trait TC[T] { - def prop: Boolean - } - - object TC { - def apply[T](implicit tc: TC[T]): TC[T] = tc - - implicit val intTC: TC[Int] = new TC[Int] { def prop = true } - } - - case class CC(s: String) - - object CC { - implicit val ccTC: TC[CC] = new TC[CC] { def prop = true } - } - - case class CC2(s: String) - - object Extra { - implicit def extraTC[T](implicit notFound: LowPriority): TC[T] = - new TC[T] { def prop = false } - } - } - - object WithIgnoring { - trait TC[T] { - def prop: Option[Boolean] - } - - trait LowPriTC { - implicit def anyTC[T]: TC[T] = new TC[T] { def prop = None } - } - - object TC extends LowPriTC { - def apply[T](implicit tc: TC[T]): TC[T] = tc - - implicit val intTC: TC[Int] = new TC[Int] { def prop = Some(true) } - } - - case class CC(s: String) - - object CC { - implicit val ccTC: TC[CC] = new TC[CC] { def prop = Some(true) } - } - - case class CC2(s: String) - - object Extra { - implicit def extraTC[T](implicit notFound: LowPriority.Ignoring[Witness.`"anyTC"`.T]): TC[T] = - new TC[T] { def prop = Some(false) } - } - } - - - def simpleInt = { - import Simple._ - import Extra._ - TC[Int] - } - def simpleCC = { - import Simple._ - import Extra._ - TC[CC] - } - def simpleString = { - import Simple._ - import Extra._ - TC[String] - } - def simpleCC2 = { - import Simple._ - import Extra._ - TC[CC2] - } - def simpleCC2_0 = { - import Simple._ - import Extra._ - - { - implicit val cc2TC: TC[CC2] = new TC[CC2] { def prop = true } - TC[CC2] - } - } - - def withIgnoringInt = { - import WithIgnoring._ - import Extra._ - TC[Int] - } - def withIgnoringCC = { - import WithIgnoring._ - import Extra._ - TC[CC] - } - def withIgnoringString = { - import WithIgnoring._ - import Extra._ - TC[String] - } - def withIgnoringCC2 = { - import WithIgnoring._ - import Extra._ - TC[CC2] - } - def withIgnoringCC2_0 = { - import WithIgnoring._ - import Extra._ - - { - implicit val cc2TC: TC[CC2] = new TC[CC2] { def prop = Some(true) } - TC[CC2] - } - } - - val tests = TestSuite { - 'simple - { - // `Extra` provides extra implicit instances of `TC[T]` - // We check here that these do not take precedence over the already existing implicit instances. - - assert(simpleInt.prop) - assert(simpleCC.prop) - assert(!simpleString.prop) - assert(!simpleCC2.prop) - - assert(simpleCC2_0.prop) - } - - 'withIgnoring - { - import WithIgnoring._ - import Extra._ - - // `Extra` provides extra implicit instances of `TC[T]` - // We check here that these do not take precedence over the already existing implicit instances. - - assert(withIgnoringInt.prop == Some(true)) - assert(withIgnoringCC.prop == Some(true)) - assert(withIgnoringString.prop == Some(false)) - assert(withIgnoringCC2.prop == Some(false)) - - assert(withIgnoringCC2_0.prop == Some(true)) - } - } - -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/PriorityTests.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/PriorityTests.scala deleted file mode 100644 index 0ce5ee1f..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/PriorityTests.scala +++ /dev/null @@ -1,25 +0,0 @@ -package io.circe.simplegeneric - -import io.circe.Decoder -import utest._ - -object PriorityTests extends TestSuite { - import PriorityTestsDefn._ - - val tests = TestSuite { - 'dontOverride - { - Decoder[CC] match { - case _: Flag => - case _ => throw new Exception(s"Default Decoder was overridden") - } - } - - 'doOverride - { - Decoder[CC2] match { - case _: Flag => throw new Exception(s"Can't happen") - case _ => - } - } - } - -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/PriorityTestsDefn.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/PriorityTestsDefn.scala deleted file mode 100644 index 791446a7..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/PriorityTestsDefn.scala +++ /dev/null @@ -1,20 +0,0 @@ -package io.circe.simplegeneric - -import io.circe.{Decoder, HCursor} - -object PriorityTestsDefn { - trait Flag - - case class CC(s: String, i: Int) - - object CC { - implicit val decode: Decoder[CC] = new Decoder[CC] with Flag { - def apply(c: HCursor) = - Decoder[(String, Int)].apply(c).right.map { case (s, i) => - CC(s, i) - } - } - } - - case class CC2(i: Int, s: String) -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/ProductEncodeTests.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/ProductEncodeTests.scala deleted file mode 100644 index 4060a7c0..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/ProductEncodeTests.scala +++ /dev/null @@ -1,237 +0,0 @@ -package io.circe.simplegeneric - -import utest._ -import org.scalacheck.{ Arbitrary, Prop } -import org.scalacheck.Shapeless._ -import shapeless._ -import io.circe.simplegeneric.derive.{ HListProductEncoder, JsonProductCodecFor, MkEncoder, ProductEncoder } -import io.circe.{ Encoder, Json, KeyEncoder } -import io.circe.syntax._ -import Util._ - - -object ProductEncodeTests extends TestSuite { - - case class WrappedMap(m: Map[String, Json]) - - lazy val expectedEmptyEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[Empty.type], - Default.AsOptions[Empty.type], - HListProductEncoder.hnil - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedEmptyCCEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[EmptyCC], - Default.AsOptions[EmptyCC], - HListProductEncoder.hnil - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedSimpleEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[Simple], - Default.AsOptions[Simple], - HListProductEncoder.hcons( - Witness('i), - Encoder.encodeInt, - HListProductEncoder.hcons( - Witness('s), - Encoder.encodeString, - HListProductEncoder.hcons( - Witness('blah), - Encoder.encodeBoolean, - HListProductEncoder.hnil - ) - ) - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedComposedEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[Composed], - Default.AsOptions[Composed], - HListProductEncoder.hcons( - Witness('foo), - expectedSimpleEncoder, - HListProductEncoder.hcons( - Witness('other), - Encoder.encodeString, - HListProductEncoder.hnil - ) - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedSimpleWithJsEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[SimpleWithJs], - Default.AsOptions[SimpleWithJs], - HListProductEncoder.hcons( - Witness('i), - Encoder.encodeInt, - HListProductEncoder.hcons( - Witness('s), - Encoder.encodeString, - HListProductEncoder.hcons( - Witness('v), - Encoder.encodeJson, - HListProductEncoder.hnil - ) - ) - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedWrappedMapEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[WrappedMap], - Default.AsOptions[WrappedMap], - HListProductEncoder.hcons( - Witness('m), - Encoder.encodeMapLike[Map, String, Json](KeyEncoder.encodeKeyString, Encoder.encodeJson), - HListProductEncoder.hnil - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedOIEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[OI], - Default.AsOptions[OI], - HListProductEncoder.hcons( - Witness('oi), - Encoder.encodeOption[Int](Encoder.encodeInt), - HListProductEncoder.hnil - ) - ), - JsonProductCodecFor.default - ).encoder - - - def compareEncoders[T: Arbitrary](first: Encoder[T], second: Encoder[T]): Unit = - Prop.forAll{ - t: T => - first(t) == second(t) - }.validate - - def jsonIs[T: Encoder](t: T, json: Json): Unit = { - assert(t.asJson == json) - } - - - val tests = TestSuite { - - 'codec { - 'empty - { - compareEncoders(Encoder[Empty.type], expectedEmptyEncoder) - } - - 'emptyCC - { - compareEncoders(Encoder[EmptyCC], expectedEmptyCCEncoder) - } - - 'simple - { - compareEncoders(Encoder[Simple], expectedSimpleEncoder) - } - - 'composed - { - compareEncoders(Encoder[Composed], expectedComposedEncoder) - } - - // Disabled, Arbitrary Json generation seems to take forever - // 'simpleWithJs - { - // compareEncoders(Encoder[SimpleWithJs], expectedSimpleWithJsEncoder) - // } - - // Looks like not enough WrappedMap can be generated - // 'wrappedMap - { - // val arb = Gen.resize(1000, Arbitrary.arbitrary[WrappedMap]) - // compareEncoders(Encoder[WrappedMap], expectedWrappedMapEncoder)(Arbitrary(arb)) - // } - - 'withOption - { - compareEncoders(Encoder[OI], expectedOIEncoder) - } - } - - 'output { - 'empty - { - jsonIs(Empty, Json.obj()) - } - - 'emptyCC - { - jsonIs(EmptyCC(), Json.obj()) - } - - 'simple - { - jsonIs( - Simple(41, "aa", blah = false), - Json.obj( - "i" -> Json.fromInt(41), - "s" -> Json.fromString("aa"), - "blah" -> Json.fromBoolean(false) - ) - ) - } - - 'composed - { - jsonIs( - Composed(Simple(41, "aa", blah = false), "bbb"), - Json.obj( - "foo" -> Json.obj( - "i" -> Json.fromInt(41), - "s" -> Json.fromString("aa"), - "blah" -> Json.fromBoolean(false) - ), - "other" -> Json.fromString("bbb") - ) - ) - } - - 'simpleWithJs - { - jsonIs( - SimpleWithJs(41, "aa", Json.arr(Json.fromInt(10), Json.obj("a" -> Json.fromBoolean(true)))), - Json.obj( - "i" -> Json.fromInt(41), - "s" -> Json.fromString("aa"), - "v" -> Json.arr(Json.fromInt(10), Json.obj("a" -> Json.fromBoolean(true))) - ) - ) - } - - 'wrappedMap - { - jsonIs( - WrappedMap(Map( - "aa" -> Json.arr(Json.fromInt(10), Json.obj("a" -> Json.fromBoolean(true))), - "bb" -> Json.obj("c" -> Json.fromBoolean(false)) - )), - Json.obj( - "m" -> Json.obj( - "aa" -> Json.arr(Json.fromInt(10), Json.obj("a" -> Json.fromBoolean(true))), - "bb" -> Json.obj("c" -> Json.fromBoolean(false)) - ) - ) - ) - } - } - - } - -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/ShapelessTests.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/ShapelessTests.scala deleted file mode 100644 index 24c540a1..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/ShapelessTests.scala +++ /dev/null @@ -1,87 +0,0 @@ -package io.circe.simplegeneric - -import org.scalacheck.{ Arbitrary, Prop } -import shapeless.test.illTyped -import utest._ -import Util._ -import io.circe.{ Decoder, Encoder, Json, parser => Parse } -import io.circe.syntax._ - - -object ShapelessTests extends TestSuite { - private def toFromJson[T: Encoder : Decoder](t: T): Decoder.Result[T] = t.asJson.as[T] - - private def sameAfterBeforeSerialization[T: Arbitrary : Encoder : Decoder]: Unit = - Prop.forAll { - t: T => - toFromJson(t) == Right(t) - }.validate - - import org.scalacheck.Shapeless._ - - val tests = TestSuite { - 'serializeDeserialize { - 'empty - { - sameAfterBeforeSerialization[Empty.type] - } - - 'emptyCC - { - sameAfterBeforeSerialization[EmptyCC] - } - - 'simple - { - sameAfterBeforeSerialization[Simple] - } - - 'composed - { - sameAfterBeforeSerialization[Composed] - } - - 'twiceComposed - { - sameAfterBeforeSerialization[TwiceComposed] - } - - 'composedOptList - { - sameAfterBeforeSerialization[ComposedOptList] - } - - 'nowThree - { - sameAfterBeforeSerialization[NowThree] - } - - 'oi - { - sameAfterBeforeSerialization[OI] - } - - 'oiLoose - { - val json = Parse.parse("{}").right.toOption.get - // assert macro crashes if result is substituted by its value below - val result = json.as[OI] - assert(result == Right(OI(None))) - } - - 'base - { - sameAfterBeforeSerialization[Base] - } - - 'simpleWithJsDummy - { - Encoder[Json] - Decoder[Json] - Encoder[SimpleWithJs] - Decoder[SimpleWithJs] - // Arbitrary[SimpleWithJs] doesn't seem fine - // sameAfterBeforeSerialization[SimpleWithJs] - } - } - } - - illTyped(" Encoder[NoArbitraryType] ") - illTyped(" Decoder[NoArbitraryType] ") - illTyped(" Encoder[ShouldHaveNoArb] ") - illTyped(" Decoder[ShouldHaveNoArb] ") - illTyped(" Encoder[ShouldHaveNoArbEither] ") - illTyped(" Decoder[ShouldHaveNoArbEither] ") - illTyped(" Encoder[BaseNoArb] ") - illTyped(" Decoder[BaseNoArb] ") - -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/SumEncodeTests.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/SumEncodeTests.scala deleted file mode 100644 index ec080953..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/SumEncodeTests.scala +++ /dev/null @@ -1,148 +0,0 @@ -package io.circe.simplegeneric - -import utest._ -import org.scalacheck.Shapeless._ -import shapeless._ -import derive._ -import io.circe.{Encoder, Json} - - -object SumEncodeTests extends TestSuite { - import ProductEncodeTests.{ compareEncoders, jsonIs } - - lazy val expectedBaseISEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[BaseIS], - Default.AsOptions[BaseIS], - Lazy( - HListProductEncoder.hcons( - Witness('i), - Strict(Encoder.encodeInt), - HListProductEncoder.hcons( - Witness('s), - Strict(Encoder.encodeString), - HListProductEncoder.hnil - ) - ) - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedBaseDBEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[BaseDB], - Default.AsOptions[BaseDB], - Lazy( - HListProductEncoder.hcons( - Witness('d), - Strict(Encoder.encodeDouble), - HListProductEncoder.hcons( - Witness('b), - Strict(Encoder.encodeBoolean), - HListProductEncoder.hnil - ) - ) - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedBaseLastEncoder = - MkEncoder.product( - ProductEncoder.generic( - LabelledGeneric[BaseLast], - Default.AsOptions[BaseLast], - Lazy( - HListProductEncoder.hcons( - Witness('c), - Strict(ProductEncodeTests.expectedSimpleEncoder), - HListProductEncoder.hnil - ) - ) - ), - JsonProductCodecFor.default - ).encoder - - lazy val expectedBaseEncoder = expectedBaseEncoderFor(JsonSumCodecFor.default) - lazy val expectedBaseEncoderTypeField = expectedBaseEncoderFor(JsonSumCodecFor(JsonSumCodec.typeField)) - def expectedBaseEncoderFor(codecFor: JsonSumCodecFor[Base]) = - MkEncoder.sum( - SumEncoder.generic( - LabelledGeneric[Base], - CoproductSumEncoder.ccons( - Witness('BaseDB), - expectedBaseDBEncoder, - CoproductSumEncoder.ccons( - Witness('BaseIS), - expectedBaseISEncoder, - CoproductSumEncoder.ccons( - Witness('BaseLast), - expectedBaseLastEncoder, - CoproductSumEncoder.cnil - ) - ) - ) - ), - codecFor - ).encoder - - val derivedBaseEncoderTypeField = { - implicit val codecFor = JsonSumCodecFor[Base](JsonSumCodec.typeField) - Encoder[Base] - } - - val tests = TestSuite { - - 'codec { - 'base - { - compareEncoders(Encoder[Base], expectedBaseEncoder) - } - - 'baseTypeField - { - compareEncoders(derivedBaseEncoderTypeField, expectedBaseEncoderTypeField) - } - } - - 'output { - 'base - { - jsonIs( - BaseLast(Simple(41, "aa", blah = false)): Base, - Json.obj( - "BaseLast" -> Json.obj( - "c" -> Json.obj( - "i" -> Json.fromInt(41), - "s" -> Json.fromString("aa"), - "blah" -> Json.fromBoolean(false) - ) - ) - ) - ) - - jsonIs( - BaseIS(43, "aa"): Base, - Json.obj( - "BaseIS" -> Json.obj( - "i" -> Json.fromInt(43), - "s" -> Json.fromString("aa") - ) - ) - ) - - jsonIs( - BaseDB(3.2, false): Base, - Json.obj( - "BaseDB" -> Json.obj( - "d" -> Json.fromDoubleOrString(3.2), - "b" -> Json.fromBoolean(false) - ) - ) - ) - } - } - - } - -} diff --git a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/Util.scala b/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/Util.scala deleted file mode 100644 index 16fa8c61..00000000 --- a/circe-simple-generic/shared/src/test/scala/io/circe/simplegeneric/Util.scala +++ /dev/null @@ -1,15 +0,0 @@ -package io.circe.simplegeneric - -import org.scalacheck.{ Prop, Test } -import utest._ - -object Util { - - implicit class PropExtensions(val prop: Prop) extends AnyVal { - def validate: Unit = { - val result = Test.check(Test.Parameters.default, prop) - assert(result.passed) - } - } - -} diff --git a/core/js/src/main/scala/plotly/element/PlotlyJavaTimeConversions.scala b/core/js/src/main/scala/plotly/element/PlotlyJavaTimeConversions.scala new file mode 100644 index 00000000..1115fc91 --- /dev/null +++ b/core/js/src/main/scala/plotly/element/PlotlyJavaTimeConversions.scala @@ -0,0 +1,5 @@ +package plotly.element + +trait PlotlyJavaTimeConversions { + // Empty since the java.time classes are not available in ScalaJS +} diff --git a/core/jvm/src/main/scala/plotly/element/PlotlyJavaTimeConversions.scala b/core/jvm/src/main/scala/plotly/element/PlotlyJavaTimeConversions.scala new file mode 100644 index 00000000..bf0d166a --- /dev/null +++ b/core/jvm/src/main/scala/plotly/element/PlotlyJavaTimeConversions.scala @@ -0,0 +1,41 @@ +package plotly.element + +import java.time._ +import plotly.element.{LocalDateTime => PlotlyLocalDateTime} + +import scala.language.implicitConversions + +trait PlotlyJavaTimeConversions { + + implicit def fromJavaLocalDateTime(javaLocalDateTime: java.time.LocalDateTime): PlotlyLocalDateTime = + PlotlyLocalDateTime( + javaLocalDateTime.getYear, + javaLocalDateTime.getMonthValue, + javaLocalDateTime.getDayOfMonth, + javaLocalDateTime.getHour, + javaLocalDateTime.getMinute, + javaLocalDateTime.getSecond + ) + + implicit def fromJavaInstant(javaInstant: Instant): PlotlyLocalDateTime = + fromJavaLocalDateTime(javaInstant.atOffset(ZoneOffset.UTC).toLocalDateTime) + + implicit def fromJavaLocalDate(javaLocalDate: LocalDate): PlotlyLocalDateTime = + fromJavaLocalDateTime(javaLocalDate.atStartOfDay) + + /** Implicit conversions in this object convert to `plotly.element.LocalDateTime` by simply dropping timezone/offset + * information. This can lead to unexpected behaviour, particularly for datasets with varying offsets and timezones. + * It will generally be safer to convert your data to `java.time.LocalDateTime` in the appropriate timezone/offset, + * and then use the `PlotlyJavaTimeConversions.fromJavaLocalDateTime` implicit conversion. + */ + object UnsafeImplicitConversions { + + implicit def fromJavaOffsetDateTime(javaOffsetDateTime: OffsetDateTime): PlotlyLocalDateTime = + fromJavaLocalDateTime(javaOffsetDateTime.toLocalDateTime) + + implicit def fromJavaZonedDateTime(javaZonedDateTime: ZonedDateTime): PlotlyLocalDateTime = + fromJavaLocalDateTime(javaZonedDateTime.toLocalDateTime) + + } + +} diff --git a/core/shared/src/main/scala-2.12/plotly/MutableSequenceImplicitConversions.scala b/core/shared/src/main/scala-2.12/plotly/MutableSequenceImplicitConversions.scala new file mode 100644 index 00000000..0e4650ce --- /dev/null +++ b/core/shared/src/main/scala-2.12/plotly/MutableSequenceImplicitConversions.scala @@ -0,0 +1,5 @@ +package plotly + +trait MutableSequenceImplicitConversions { + // Unneccessary in Scala 2.12, since the `Seq` alias refers to the supertype for mutable and immutable sequences +} diff --git a/core/shared/src/main/scala-2.13/plotly/MutableSequenceImplicitConversions.scala b/core/shared/src/main/scala-2.13/plotly/MutableSequenceImplicitConversions.scala new file mode 100644 index 00000000..c8edf055 --- /dev/null +++ b/core/shared/src/main/scala-2.13/plotly/MutableSequenceImplicitConversions.scala @@ -0,0 +1,28 @@ +package plotly + +import plotly.Sequence.{DateTimes, Doubles, NestedDoubles, NestedInts, Strings} +import plotly.element.LocalDateTime + +import scala.collection.{Seq => BaseScalaSeq} +import scala.language.implicitConversions + +trait MutableSequenceImplicitConversions { + + implicit def fromMutableDoubleSeq(s: BaseScalaSeq[Double]): Sequence = + Doubles(s.toSeq) + implicit def fromMutableFloatSeq(s: BaseScalaSeq[Float]): Sequence = + Doubles(s.map(_.toDouble).toSeq) + implicit def fromMutableIntSeq(s: BaseScalaSeq[Int]): Sequence = + Doubles(s.map(_.toDouble).toSeq) + implicit def fromMutableLongSeq(s: BaseScalaSeq[Long]): Sequence = + Doubles(s.map(_.toDouble).toSeq) + implicit def fromMutableNestedDoubleSeq(s: BaseScalaSeq[BaseScalaSeq[Double]]): Sequence = + NestedDoubles(s.map(_.toSeq).toSeq) + implicit def fromMutableNestedIntSeq(s: BaseScalaSeq[BaseScalaSeq[Int]]): Sequence = + NestedInts(s.map(_.toSeq).toSeq) + implicit def fromMutableStringSeq(s: BaseScalaSeq[String]): Sequence = + Strings(s.toSeq) + implicit def fromMutableDateTimes(seq: BaseScalaSeq[LocalDateTime]): Sequence = + DateTimes(seq.toSeq) + +} diff --git a/core/shared/src/main/scala/plotly/Config.scala b/core/shared/src/main/scala/plotly/Config.scala new file mode 100755 index 00000000..a886516b --- /dev/null +++ b/core/shared/src/main/scala/plotly/Config.scala @@ -0,0 +1,27 @@ +package plotly + +import java.lang.{Boolean => JBoolean} +import dataclass.data + +@data(optionSetters = true) class Config( + editable: Option[Boolean] = None, + responsive: Option[Boolean] = None, + showEditInChartStudio: Option[Boolean] = None, + plotlyServerURL: Option[String] = None +) + +object Config { + @deprecated("Use Config() and chain-call .with* methods on it instead", "0.8.0") + def apply( + editable: JBoolean = null, + responsive: JBoolean = null, + showEditInChartStudio: JBoolean = null, + plotlyServerURL: String = null + ): Config = + new Config( + Option(editable), + Option(responsive), + Option(showEditInChartStudio), + Option(plotlyServerURL) + ) +} diff --git a/core/shared/src/main/scala/plotly/Range.scala b/core/shared/src/main/scala/plotly/Range.scala new file mode 100644 index 00000000..c4221cc1 --- /dev/null +++ b/core/shared/src/main/scala/plotly/Range.scala @@ -0,0 +1,17 @@ +package plotly + +import plotly.element.LocalDateTime + +import scala.language.implicitConversions + +sealed abstract class Range extends Product with Serializable + +object Range { + final case class Doubles(range: (Double, Double)) extends Range + final case class DateTimes(range: (LocalDateTime, LocalDateTime)) extends Range + + implicit def fromDoubleTuple(t: (Double, Double)): Range = + Doubles(t) + implicit def fromDateTimes(t: (LocalDateTime, LocalDateTime)): Range = + DateTimes(t) +} diff --git a/core/shared/src/main/scala/plotly/Sequence.scala b/core/shared/src/main/scala/plotly/Sequence.scala old mode 100644 new mode 100755 index c3d0ff90..f40077fa --- a/core/shared/src/main/scala/plotly/Sequence.scala +++ b/core/shared/src/main/scala/plotly/Sequence.scala @@ -2,12 +2,16 @@ package plotly import plotly.element.LocalDateTime +import scala.language.implicitConversions + sealed abstract class Sequence extends Product with Serializable -object Sequence { - final case class Doubles(seq: Seq[Double]) extends Sequence - final case class Strings(seq: Seq[String]) extends Sequence - final case class DateTimes(seq: Seq[LocalDateTime]) extends Sequence +object Sequence extends MutableSequenceImplicitConversions { + final case class Doubles(seq: Seq[Double]) extends Sequence + final case class NestedDoubles(seq: Seq[Seq[Double]]) extends Sequence + final case class NestedInts(seq: Seq[Seq[Int]]) extends Sequence + final case class Strings(seq: Seq[String]) extends Sequence + final case class DateTimes(seq: Seq[LocalDateTime]) extends Sequence implicit def fromDoubleSeq(s: Seq[Double]): Sequence = Doubles(s) @@ -17,6 +21,10 @@ object Sequence { Doubles(s.map(_.toDouble)) implicit def fromLongSeq(s: Seq[Long]): Sequence = Doubles(s.map(_.toDouble)) + implicit def fromNestedDoubleSeq(s: Seq[Seq[Double]]): Sequence = + NestedDoubles(s) + implicit def fromNestedIntSeq(s: Seq[Seq[Int]]): Sequence = + NestedInts(s) implicit def fromStringSeq(s: Seq[String]): Sequence = Strings(s) implicit def fromDateTimes(seq: Seq[LocalDateTime]): Sequence = diff --git a/core/shared/src/main/scala/plotly/Trace.scala b/core/shared/src/main/scala/plotly/Trace.scala old mode 100644 new mode 100755 index 402856de..a8a8d03c --- a/core/shared/src/main/scala/plotly/Trace.scala +++ b/core/shared/src/main/scala/plotly/Trace.scala @@ -2,54 +2,74 @@ package plotly import scala.language.implicitConversions -import java.lang.{ Boolean => JBoolean, Double => JDouble } +import java.lang.{Boolean => JBoolean, Double => JDouble} +import dataclass._ import plotly.element._ sealed abstract class Trace extends Product with Serializable -final case class Scatter( - x: Option[Sequence], - y: Option[Sequence], - text: Option[Seq[String]], - mode: Option[ScatterMode], - marker: Option[Marker], - line: Option[Line], - textposition: Option[TextPosition], - textfont: Option[TextFont], - name: Option[String], - connectgaps: Option[Boolean], - xaxis: Option[AxisReference], - yaxis: Option[AxisReference], - fill: Option[Fill], - error_x: Option[Error], - error_y: Option[Error], - showlegend: Option[Boolean] +@data(optionSetters = true) class Scatter( + x: Option[Sequence] = None, + y: Option[Sequence] = None, + text: Option[OneOrSeq[String]] = None, + mode: Option[ScatterMode] = None, + marker: Option[Marker] = None, + line: Option[Line] = None, + textposition: Option[TextPosition] = None, + textfont: Option[TextFont] = None, + name: Option[String] = None, + connectgaps: Option[Boolean] = None, + xaxis: Option[AxisReference] = None, + yaxis: Option[AxisReference] = None, + fill: Option[Fill] = None, + error_x: Option[Error] = None, + error_y: Option[Error] = None, + showlegend: Option[Boolean] = None, + fillcolor: Option[OneOrSeq[Color]] = None, + hoverinfo: Option[HoverInfo] = None, + hoveron: Option[HoverOn] = None, + stackgroup: Option[String] = None, + groupnorm: Option[GroupNorm] = None, + @since("0.8.2") + hovertemplate: Option[OneOrSeq[String]] = None ) extends Trace object Scatter { + def apply(x: Sequence, y: Sequence): Scatter = + Scatter().withX(x).withY(y) + + def apply(y: Sequence): Scatter = + Scatter().withY(y) + + @deprecated("Use Scatter() and chain-call .with* methods on it instead", "0.8.0") def apply( - values: Sequence = null, - secondValues: Sequence = null, - text: Seq[String] = null, - mode: ScatterMode = null, - marker: Marker = null, - line: Line = null, - textposition: TextPosition = null, - textfont: TextFont = null, - name: String = null, - connectgaps: JBoolean = null, - xaxis: AxisReference = null, - yaxis: AxisReference = null, - fill: Fill = null, - error_x: Error = null, - error_y: Error = null, - showlegend: JBoolean = null + values: Sequence = null, + secondValues: Sequence = null, + text: OneOrSeq[String] = null, + mode: ScatterMode = null, + marker: Marker = null, + line: Line = null, + textposition: TextPosition = null, + textfont: TextFont = null, + name: String = null, + connectgaps: JBoolean = null, + xaxis: AxisReference = null, + yaxis: AxisReference = null, + fill: Fill = null, + error_x: Error = null, + error_y: Error = null, + showlegend: JBoolean = null, + fillcolor: OneOrSeq[Color] = null, + hoverinfo: HoverInfo = null, + hoveron: HoverOn = null, + stackgroup: String = null, + groupnorm: GroupNorm = null ): Scatter = { val (xOpt, yOpt) = Option(secondValues) match { case Some(y) => (Option(values), Some(y)) - case None => (None, Option(values)) + case None => (None, Option(values)) } Scatter( @@ -62,91 +82,142 @@ object Scatter { Option(textposition), Option(textfont), Option(name), - Option(connectgaps) .map(x => x: Boolean), + Option(connectgaps).map(x => x: Boolean), Option(xaxis), Option(yaxis), Option(fill), Option(error_x), Option(error_y), - Option(showlegend) .map(b => b: Boolean) + Option(showlegend).map(b => b: Boolean), + Option(fillcolor), + Option(hoverinfo), + Option(hoveron), + Option(stackgroup), + Option(groupnorm) ) } } -case class Box( - y: Option[Sequence], - x: Option[Sequence], - boxpoints: Option[BoxPoints], - jitter: Option[Double], - pointpos: Option[Double], - name: Option[String], - marker: Option[Marker], - orientation: Option[Orientation], - whiskerwidth: Option[Double], - boxmean: Option[BoxMean], - fillcolor: Option[OneOrSeq[Color]], - line: Option[Line], - showlegend: Option[Boolean] +@data(optionSetters = true) class Box( + y: Option[Sequence] = None, + x: Option[Sequence] = None, + boxpoints: Option[BoxPoints] = None, + jitter: Option[Double] = None, + pointpos: Option[Double] = None, + name: Option[String] = None, + marker: Option[Marker] = None, + orientation: Option[Orientation] = None, + whiskerwidth: Option[Double] = None, + boxmean: Option[BoxMean] = None, + fillcolor: Option[OneOrSeq[Color]] = None, + line: Option[Line] = None, + showlegend: Option[Boolean] = None, + @since("0.8.2") + hovertemplate: Option[OneOrSeq[String]] = None ) extends Trace object Box { + def apply(y: Sequence): Box = + Box().withY(y) + + def apply(y: Sequence, x: Sequence): Box = + Box().withY(y).withX(x) + + @deprecated("Use Box() and chain-call .with* methods on it instead", "0.8.0") def apply( - y: Sequence = null, - x: Sequence = null, - boxpoints: BoxPoints = null, - jitter: JDouble = null, - pointpos: JDouble = null, - name: String = null, - marker: Marker = null, - orientation: Orientation = null, - whiskerwidth: JDouble = null, - boxmean: BoxMean = null, - fillcolor: OneOrSeq[Color] = null, - line: Line = null, - showlegend: JBoolean = null + y: Sequence = null, + x: Sequence = null, + boxpoints: BoxPoints = null, + jitter: JDouble = null, + pointpos: JDouble = null, + name: String = null, + marker: Marker = null, + orientation: Orientation = null, + whiskerwidth: JDouble = null, + boxmean: BoxMean = null, + fillcolor: OneOrSeq[Color] = null, + line: Line = null, + showlegend: JBoolean = null ): Box = Box( Option(y), Option(x), Option(boxpoints), - Option(jitter) .map(d => d: Double), - Option(pointpos) .map(d => d: Double), + Option(jitter).map(d => d: Double), + Option(pointpos).map(d => d: Double), Option(name), Option(marker), Option(orientation), - Option(whiskerwidth) .map(d => d: Double), + Option(whiskerwidth).map(d => d: Double), Option(boxmean), Option(fillcolor), Option(line), - Option(showlegend) .map(b => b: Boolean) + Option(showlegend).map(b => b: Boolean) ) } -final case class Bar( - x: Sequence, - y: Sequence, - name: Option[String], - text: Option[Seq[String]], - marker: Option[Marker], - orientation: Option[Orientation], - xaxis: Option[AxisReference], - yaxis: Option[AxisReference], - error_y: Option[Error], - showlegend: Option[Boolean] +@data(optionSetters = true) class Image( + z: Seq[Seq[Seq[Double]]], + x0: Option[Element] = None, + y0: Option[Element] = None, + name: Option[String] = None, + text: Option[Seq[String]] = None, + opacity: Option[Double] = None, + ids: Option[Seq[String]] = None, + dx: Option[Double] = None, + dy: Option[Double] = None, + source: Option[String] = None, + hoverinfo: Option[HoverInfo] = None, + hovertemplate: Option[Seq[String]] = None, + meta: Option[String] = None, + customdata: Option[Seq[String]] = None, + xaxis: Option[AxisReference] = None, + yaxis: Option[AxisReference] = None, + colormodel: Option[ColorModel] = None, + zmax: Option[Seq[Double]] = None, + zmin: Option[Seq[Double]] = None, + hoverlabel: Option[HoverLabel] = None +) extends Trace + +@data(optionSetters = true) class Bar( + x: Sequence, + y: Sequence, + @since + name: Option[String] = None, + text: Option[Seq[String]] = None, + marker: Option[Marker] = None, + orientation: Option[Orientation] = None, + xaxis: Option[AxisReference] = None, + yaxis: Option[AxisReference] = None, + error_y: Option[Error] = None, + showlegend: Option[Boolean] = None, + hoverinfo: Option[HoverInfo] = None, + textposition: Option[BarTextPosition] = None, + opacity: Option[Double] = None, + width: Option[OneOrSeq[Double]] = None, + base: Option[OneOrSeq[Double]] = None, + @since("0.8.2") + hovertemplate: Option[OneOrSeq[String]] = None ) extends Trace object Bar { + @deprecated("Use Bar() and chain-call .with* methods on it instead", "0.8.0") def apply( - x: Sequence, - y: Sequence, - name: String = null, - text: Seq[String] = null, - marker: Marker = null, - orientation: Orientation = null, - xaxis: AxisReference = null, - yaxis: AxisReference = null, - error_y: Error = null, - showlegend: JBoolean = null + x: Sequence, + y: Sequence, + name: String = null, + text: Seq[String] = null, + marker: Marker = null, + orientation: Orientation = null, + xaxis: AxisReference = null, + yaxis: AxisReference = null, + error_y: Error = null, + showlegend: JBoolean = null, + hoverinfo: HoverInfo = null, + textposition: BarTextPosition = null, + opacity: JDouble = null, + width: OneOrSeq[Double] = null, + base: OneOrSeq[Double] = null ): Bar = Bar( x, @@ -158,43 +229,176 @@ object Bar { Option(xaxis), Option(yaxis), Option(error_y), - Option(showlegend).map(b => b: Boolean) + Option(showlegend).map(b => b: Boolean), + Option(hoverinfo), + Option(textposition), + Option(opacity).map(d => d: Double), + Option(width), + Option(base) ) } -case class Histogram( - x: Option[Sequence], - y: Option[Sequence], - opacity: Option[Double], - name: Option[String], - autobinx: Option[Boolean], - marker: Option[Marker], - xbins: Option[Bins], - histnorm: Option[HistNorm], - showlegend: Option[Boolean] +@data(optionSetters = true) class Histogram( + x: Option[Sequence] = None, + y: Option[Sequence] = None, + opacity: Option[Double] = None, + name: Option[String] = None, + autobinx: Option[Boolean] = None, + marker: Option[Marker] = None, + xbins: Option[Bins] = None, + histnorm: Option[HistNorm] = None, + showlegend: Option[Boolean] = None, + cumulative: Option[Cumulative] = None, + histfunc: Option[HistFunc] = None, + @since("0.8.2") + hovertemplate: Option[OneOrSeq[String]] = None, + @since("0.8.5") + yaxis: Option[String] = None, + hovertext: Option[OneOrSeq[String]] = None ) extends Trace object Histogram { + def apply(x: Sequence): Histogram = + Histogram().withX(x) + + def apply(x: Sequence, y: Sequence): Histogram = + Histogram().withX(x).withY(y) + + @deprecated("Use Histogram() and chain-call .with* methods on it instead", "0.8.0") def apply( - x: Sequence = null, - y: Sequence = null, - opacity: JDouble = null, - name: String = null, - autobinx: JBoolean = null, - marker: Marker = null, - xbins: Bins = null, - histnorm: HistNorm = null, - showlegend: JBoolean = null + x: Sequence = null, + y: Sequence = null, + opacity: JDouble = null, + name: String = null, + autobinx: JBoolean = null, + marker: Marker = null, + xbins: Bins = null, + histnorm: HistNorm = null, + showlegend: JBoolean = null, + cumulative: Cumulative = null, + histfunc: HistFunc = null ): Histogram = Histogram( Option(x), Option(y), - Option(opacity) .map(d => d: Double), + Option(opacity).map(d => d: Double), Option(name), - Option(autobinx) .map(b => b: Boolean), + Option(autobinx).map(b => b: Boolean), Option(marker), Option(xbins), Option(histnorm), - Option(showlegend) .map(b => b: Boolean) + Option(showlegend).map(b => b: Boolean), + Option(cumulative), + Option(histfunc) + ) +} + +@data(optionSetters = true) class Surface( + x: Option[Sequence] = None, + y: Option[Sequence] = None, + z: Option[Sequence] = None, + showscale: Option[Boolean] = None, + opacity: Option[Double] = None, + @since("0.8.2") + hovertemplate: Option[OneOrSeq[String]] = None +) extends Trace + +object Surface { + @deprecated("Use Surface() and chain-call .with* methods on it instead", "0.8.0") + def apply( + x: Sequence = null, + y: Sequence = null, + z: Sequence = null, + showscale: JBoolean = null, + opacity: JDouble = null + ): Surface = + Surface( + Option(x), + Option(y), + Option(z), + Option(showscale).map(b => b: Boolean), + Option(opacity).map(d => d: Double) + ) +} + +@data(optionSetters = true) class Heatmap( + y: Option[Sequence] = None, + x: Option[Sequence] = None, + z: Option[Sequence] = None, + autocolorscale: Option[Boolean] = None, + colorscale: Option[ColorScale] = None, + showscale: Option[Boolean] = None, + name: Option[String] = None, + @since("0.8.2") + hovertemplate: Option[OneOrSeq[String]] = None, + hoverongaps: Option[Boolean] = None +) extends Trace + +object Heatmap { + def apply(z: Sequence): Heatmap = + Heatmap().withZ(z) + + def apply(z: Sequence, x: Sequence, y: Sequence): Heatmap = + Heatmap().withZ(z).withX(x).withY(y) + + @deprecated("Use Heatmap() and chain-call .with* methods on it instead", "0.8.0") + def apply( + y: Sequence = null, + x: Sequence = null, + z: Sequence = null, + autocolorscale: JBoolean = null, + colorscale: ColorScale = null, + showscale: JBoolean = null, + name: String = null + ): Heatmap = + Heatmap( + Option(y), + Option(x), + Option(z), + Option(autocolorscale).map(b => b: Boolean), + Option(colorscale), + Option(showscale).map(b => b: Boolean), + Option(name) + ) +} + +@data(optionSetters = true) class Candlestick( + x: Option[Sequence] = None, + close: Option[Sequence] = None, + high: Option[Sequence] = None, + low: Option[Sequence] = None, + open: Option[Sequence] = None, + decreasing: Option[Marker] = None, + increasing: Option[Marker] = None, + line: Option[Marker] = None, + xaxis: Option[AxisReference] = None, + yaxis: Option[AxisReference] = None +) extends Trace + +object Candlestick { + @deprecated("Use Candlestick() and chain-call .with* methods on it instead", "0.8.0") + def apply( + x: Sequence = null, + close: Sequence = null, + high: Sequence = null, + low: Sequence = null, + open: Sequence = null, + decreasing: Marker = null, + increasing: Marker = null, + line: Marker = null, + xaxis: AxisReference = null, + yaxis: AxisReference = null + ): Candlestick = + Candlestick( + Option(x), + Option(close), + Option(high), + Option(low), + Option(open), + Option(decreasing), + Option(increasing), + Option(line), + Option(xaxis), + Option(yaxis) ) } diff --git a/core/shared/src/main/scala/plotly/element/Alignment.scala b/core/shared/src/main/scala/plotly/element/Alignment.scala new file mode 100644 index 00000000..c5043917 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/Alignment.scala @@ -0,0 +1,9 @@ +package plotly.element + +sealed abstract class Alignment(val label: String) extends Product with Serializable + +object Alignment { + case object Left extends Alignment("left") + case object Right extends Alignment("right") + case object Auto extends Alignment("auto") +} diff --git a/core/shared/src/main/scala/plotly/element/Anchor.scala b/core/shared/src/main/scala/plotly/element/Anchor.scala index a3ed33f1..c1204e0f 100644 --- a/core/shared/src/main/scala/plotly/element/Anchor.scala +++ b/core/shared/src/main/scala/plotly/element/Anchor.scala @@ -4,10 +4,10 @@ package element sealed abstract class Anchor(val label: String) extends Product with Serializable object Anchor { - case object Left extends Anchor("left") + case object Left extends Anchor("left") case object Center extends Anchor("center") - case object Right extends Anchor("right") - case object Top extends Anchor("top") + case object Right extends Anchor("right") + case object Top extends Anchor("top") case object Middle extends Anchor("middle") case object Bottom extends Anchor("bottom") } diff --git a/core/shared/src/main/scala/plotly/element/AxisAnchor.scala b/core/shared/src/main/scala/plotly/element/AxisAnchor.scala index 4cb1b37e..0ba83157 100644 --- a/core/shared/src/main/scala/plotly/element/AxisAnchor.scala +++ b/core/shared/src/main/scala/plotly/element/AxisAnchor.scala @@ -1,10 +1,12 @@ package plotly package element +import dataclass.data + sealed abstract class AxisAnchor(val label: String) extends Product with Serializable object AxisAnchor { - case class Reference(axisReference: AxisReference) extends AxisAnchor(axisReference.label) - case object Free extends AxisAnchor("free") - case object Y extends AxisAnchor("y") + @data class Reference(axisReference: AxisReference) extends AxisAnchor(axisReference.label) + case object Free extends AxisAnchor("free") + case object Y extends AxisAnchor("y") } diff --git a/core/shared/src/main/scala/plotly/element/AxisReference.scala b/core/shared/src/main/scala/plotly/element/AxisReference.scala index fd07a01a..8a326d79 100644 --- a/core/shared/src/main/scala/plotly/element/AxisReference.scala +++ b/core/shared/src/main/scala/plotly/element/AxisReference.scala @@ -4,12 +4,12 @@ package element sealed abstract class AxisReference(val label: String) extends Product with Serializable object AxisReference { - case object X extends AxisReference("x") + case object X extends AxisReference("x") case object X1 extends AxisReference("x1") case object X2 extends AxisReference("x2") case object X3 extends AxisReference("x3") case object X4 extends AxisReference("x4") - case object Y extends AxisReference("y") + case object Y extends AxisReference("y") case object Y1 extends AxisReference("y1") case object Y2 extends AxisReference("y2") case object Y3 extends AxisReference("y3") diff --git a/core/shared/src/main/scala/plotly/element/AxisType.scala b/core/shared/src/main/scala/plotly/element/AxisType.scala index 417c560e..5c7325f8 100644 --- a/core/shared/src/main/scala/plotly/element/AxisType.scala +++ b/core/shared/src/main/scala/plotly/element/AxisType.scala @@ -4,10 +4,11 @@ package element sealed abstract class AxisType(val label: String) extends Product with Serializable object AxisType { + /** Lets plotly guess from data */ - case object Default extends AxisType("-") - case object Linear extends AxisType("linear") - case object Log extends AxisType("log") - case object Date extends AxisType("date") + case object Default extends AxisType("-") + case object Linear extends AxisType("linear") + case object Log extends AxisType("log") + case object Date extends AxisType("date") case object Category extends AxisType("category") } diff --git a/core/shared/src/main/scala/plotly/element/BarTextPosition.scala b/core/shared/src/main/scala/plotly/element/BarTextPosition.scala new file mode 100644 index 00000000..964d4995 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/BarTextPosition.scala @@ -0,0 +1,11 @@ +package plotly +package element + +sealed abstract class BarTextPosition(val label: String) extends Product with Serializable + +object BarTextPosition { + case object Inside extends BarTextPosition("inside") + case object Outside extends BarTextPosition("outside") + case object Auto extends BarTextPosition("auto") + case object None extends BarTextPosition("none") +} diff --git a/core/shared/src/main/scala/plotly/element/Bins.scala b/core/shared/src/main/scala/plotly/element/Bins.scala index f580cf14..1a2b0382 100644 --- a/core/shared/src/main/scala/plotly/element/Bins.scala +++ b/core/shared/src/main/scala/plotly/element/Bins.scala @@ -1,3 +1,9 @@ package plotly.element -case class Bins(start: Double, end: Double, size: Double) +import dataclass.data + +@data class Bins( + start: Double, + end: Double, + size: Double +) diff --git a/core/shared/src/main/scala/plotly/element/BoxMean.scala b/core/shared/src/main/scala/plotly/element/BoxMean.scala index ad1948aa..f1e9a70e 100644 --- a/core/shared/src/main/scala/plotly/element/BoxMean.scala +++ b/core/shared/src/main/scala/plotly/element/BoxMean.scala @@ -1,13 +1,15 @@ package plotly package element +import dataclass.data + sealed abstract class BoxMean extends Product with Serializable object BoxMean { - case class Bool(value: Boolean) extends BoxMean + @data class Bool(value: Boolean) extends BoxMean sealed abstract class Labeled(val label: String) extends BoxMean - val True = Bool(true) + val True = Bool(true) val False = Bool(false) case object SD extends Labeled("sd") diff --git a/core/shared/src/main/scala/plotly/element/BoxPoints.scala b/core/shared/src/main/scala/plotly/element/BoxPoints.scala index 3dc67fdb..09d82326 100644 --- a/core/shared/src/main/scala/plotly/element/BoxPoints.scala +++ b/core/shared/src/main/scala/plotly/element/BoxPoints.scala @@ -1,16 +1,18 @@ package plotly package element +import dataclass.data + sealed abstract class BoxPoints extends Product with Serializable object BoxPoints { - case class Bool(value: Boolean) extends BoxPoints + @data class Bool(value: Boolean) extends BoxPoints sealed abstract class Labeled(val label: String) extends BoxPoints val False = Bool(false) - val True = Bool(true) + val True = Bool(true) - case object All extends Labeled("all") + case object All extends Labeled("all") case object SuspectedOutliers extends Labeled("suspectedoutliers") - case object Outliers extends Labeled("Outliers") // FIXME case? + case object Outliers extends Labeled("Outliers") // FIXME case? } diff --git a/core/shared/src/main/scala/plotly/element/Color.scala b/core/shared/src/main/scala/plotly/element/Color.scala index c4ccca39..84f508f2 100644 --- a/core/shared/src/main/scala/plotly/element/Color.scala +++ b/core/shared/src/main/scala/plotly/element/Color.scala @@ -1,13 +1,15 @@ package plotly package element +import dataclass.data + sealed abstract class Color extends Product with Serializable object Color { - final case class RGBA(r: Int, g: Int, b: Int, alpha: Double) extends Color + @data class RGBA(r: Int, g: Int, b: Int, alpha: Double) extends Color - final case class StringColor(color: String) extends Color + @data class StringColor(color: String) extends Color object StringColor { val colors = Set( @@ -15,11 +17,16 @@ object Color { "grey", "white", "fuchsia", - "red" + "red", + "blue", + "cls", // ??? + "pink", + "green", + "magenta" ) } - final case class RGB(r: Int, g: Int, b: Int) extends Color + @data class RGB(r: Int, g: Int, b: Int) extends Color - final case class HSL(h: Int, s: Int, l: Int) extends Color -} \ No newline at end of file + @data class HSL(h: Int, s: Int, l: Int) extends Color +} diff --git a/core/shared/src/main/scala/plotly/element/ColorModel.scala b/core/shared/src/main/scala/plotly/element/ColorModel.scala new file mode 100644 index 00000000..cec1d9a8 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/ColorModel.scala @@ -0,0 +1,11 @@ +package plotly.element + +sealed abstract class ColorModel(val label: String) extends Product with Serializable + +object ColorModel { + case object RGB extends ColorModel("rgb") + case object RGBA extends ColorModel("rgba") + case object RGBA256 extends ColorModel("rgba256") + case object HSL extends ColorModel("hsl") + case object HSLA extends ColorModel("hsla") +} diff --git a/core/shared/src/main/scala/plotly/element/ColorScale.scala b/core/shared/src/main/scala/plotly/element/ColorScale.scala new file mode 100755 index 00000000..c99d9474 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/ColorScale.scala @@ -0,0 +1,9 @@ +package plotly.element +import dataclass.data + +sealed abstract class ColorScale extends Product with Serializable + +object ColorScale { + @data class CustomScale(values: Seq[(Double, Color)]) extends ColorScale + @data class NamedScale(name: String) extends ColorScale +} diff --git a/core/shared/src/main/scala/plotly/element/Cumulative.scala b/core/shared/src/main/scala/plotly/element/Cumulative.scala new file mode 100644 index 00000000..4a64d110 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/Cumulative.scala @@ -0,0 +1,5 @@ +package plotly.element + +import dataclass.data + +@data class Cumulative(enabled: Boolean) diff --git a/core/shared/src/main/scala/plotly/element/Element.scala b/core/shared/src/main/scala/plotly/element/Element.scala index a6ea4e5c..d7de92b4 100644 --- a/core/shared/src/main/scala/plotly/element/Element.scala +++ b/core/shared/src/main/scala/plotly/element/Element.scala @@ -1,6 +1,8 @@ package plotly package element +import scala.language.implicitConversions + sealed abstract class Element extends Product with Serializable object Element { diff --git a/core/shared/src/main/scala/plotly/element/Error.scala b/core/shared/src/main/scala/plotly/element/Error.scala index e16f3f96..4f0ae7ab 100644 --- a/core/shared/src/main/scala/plotly/element/Error.scala +++ b/core/shared/src/main/scala/plotly/element/Error.scala @@ -1,24 +1,28 @@ package plotly package element -import java.lang.{ Boolean => JBoolean, Double => JDouble } +import java.lang.{Boolean => JBoolean, Double => JDouble} + +import dataclass._ sealed abstract class Error(val `type`: String) extends Product with Serializable object Error { - case class Data( - array: Seq[Double], - visible: Option[Boolean], - symmetric: Option[Boolean], - arrayminus: Option[Seq[Double]] + @data(optionSetters = true) class Data( + array: Seq[Double], + @since + visible: Option[Boolean] = None, + symmetric: Option[Boolean] = None, + arrayminus: Option[Seq[Double]] = None ) extends Error("data") object Data { + @deprecated("Use Data(array) and chain-call .with* methods on it instead", "0.8.0") def apply( - array: Seq[Double], - visible: JBoolean = null, - symmetric: JBoolean = null, - arrayminus: Seq[Double] = null + array: Seq[Double], + visible: JBoolean = null, + symmetric: JBoolean = null, + arrayminus: Seq[Double] = null ): Data = Data( array, @@ -28,19 +32,21 @@ object Error { ) } - case class Percent( - value: Double, - visible: Option[Boolean], - symmetric: Option[Boolean], - valueminus: Option[Double] + @data(optionSetters = true) class Percent( + value: Double, + @since + visible: Option[Boolean] = None, + symmetric: Option[Boolean] = None, + valueminus: Option[Double] = None ) extends Error("percent") object Percent { + @deprecated("Use Percent(value) and chain-call .with* methods on it instead", "0.8.0") def apply( - value: Double, - visible: JBoolean = null, - symmetric: JBoolean = null, - valueminus: JDouble = null + value: Double, + visible: JBoolean = null, + symmetric: JBoolean = null, + valueminus: JDouble = null ): Percent = Percent( value, @@ -50,21 +56,22 @@ object Error { ) } - case class Constant( - value: Double, - color: Option[String], - thickness: Option[Double], - opacity: Option[Double], - width: Option[Double] + @data(optionSetters = true) class Constant( + value: Double, + color: Option[String] = None, + thickness: Option[Double] = None, + opacity: Option[Double] = None, + width: Option[Double] = None ) extends Error("constant") object Constant { + @deprecated("Use Constant(value) and chain-call .with* methods on it instead", "0.8.0") def apply( - value: Double, - color: String = null, - thickness: JDouble = null, + value: Double, + color: String = null, + thickness: JDouble = null, opacity: JDouble = null, - width: JDouble = null + width: JDouble = null ): Constant = Constant( value, diff --git a/core/shared/src/main/scala/plotly/element/Fill.scala b/core/shared/src/main/scala/plotly/element/Fill.scala index 9ad76c24..438cc58c 100644 --- a/core/shared/src/main/scala/plotly/element/Fill.scala +++ b/core/shared/src/main/scala/plotly/element/Fill.scala @@ -4,6 +4,11 @@ package element sealed abstract class Fill(val label: String) extends Product with Serializable object Fill { + case object None extends Fill("none") + case object ToZeroX extends Fill("tozerox") case object ToZeroY extends Fill("tozeroy") + case object ToNextX extends Fill("tonextx") case object ToNextY extends Fill("tonexty") + case object ToSelf extends Fill("toself") + case object ToNext extends Fill("tonext") } diff --git a/core/shared/src/main/scala/plotly/element/GroupNorm.scala b/core/shared/src/main/scala/plotly/element/GroupNorm.scala new file mode 100644 index 00000000..03deecbc --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/GroupNorm.scala @@ -0,0 +1,8 @@ +package plotly.element + +sealed abstract class GroupNorm(val label: String) extends Product with Serializable + +object GroupNorm { + case object Fraction extends GroupNorm("fraction") + case object Percent extends GroupNorm("percent") +} diff --git a/core/shared/src/main/scala/plotly/element/HistFunc.scala b/core/shared/src/main/scala/plotly/element/HistFunc.scala new file mode 100644 index 00000000..4aabca56 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/HistFunc.scala @@ -0,0 +1,13 @@ +package plotly.element + +sealed abstract class HistFunc(val label: String) extends Product with Serializable + +object HistFunc { + + case object Count extends HistFunc("count") + case object Sum extends HistFunc("sum") + case object Average extends HistFunc("avg") + case object Min extends HistFunc("min") + case object Max extends HistFunc("max") + +} diff --git a/core/shared/src/main/scala/plotly/element/HistNorm.scala b/core/shared/src/main/scala/plotly/element/HistNorm.scala index 694062fc..fa619ce7 100644 --- a/core/shared/src/main/scala/plotly/element/HistNorm.scala +++ b/core/shared/src/main/scala/plotly/element/HistNorm.scala @@ -3,9 +3,9 @@ package plotly.element sealed abstract class HistNorm(val label: String) extends Product with Serializable object HistNorm { - case object Count extends HistNorm("count") - case object Percent extends HistNorm("percent") - case object Probability extends HistNorm("probability") - case object Density extends HistNorm("density") + case object Count extends HistNorm("count") + case object Percent extends HistNorm("percent") + case object Probability extends HistNorm("probability") + case object Density extends HistNorm("density") case object ProbabilityDensity extends HistNorm("probability density") } diff --git a/core/shared/src/main/scala/plotly/element/HoverInfo.scala b/core/shared/src/main/scala/plotly/element/HoverInfo.scala new file mode 100644 index 00000000..de8d8b26 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/HoverInfo.scala @@ -0,0 +1,36 @@ +package plotly.element + +sealed abstract class HoverInfo extends Product with Serializable { + def label: String +} + +object HoverInfo { + + def all: HoverInfo = All + def none: HoverInfo = None + def skip: HoverInfo = Skip + def apply(elements: Element*): HoverInfo = + Combination(elements) + + sealed abstract class Element(override val label: String) extends HoverInfo + + case object X extends Element("x") + case object Y extends Element("y") + case object Z extends Element("z") + case object Text extends Element("text") + case object Name extends Element("name") + case object Color extends Element("color") + + case object All extends HoverInfo { + def label = "all" + } + val None = Combination(Nil) + case object Skip extends HoverInfo { + def label = "skip" + } + + final case class Combination(elements: Seq[Element]) extends HoverInfo { + def label: String = elements.map(_.label).mkString("+") + } + +} diff --git a/core/shared/src/main/scala/plotly/element/HoverLabel.scala b/core/shared/src/main/scala/plotly/element/HoverLabel.scala new file mode 100644 index 00000000..15342160 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/HoverLabel.scala @@ -0,0 +1,12 @@ +package plotly.element + +import dataclass.data + +@data(optionSetters = true) class HoverLabel( + bgcolor: Option[OneOrSeq[Color]] = None, + bordercolor: Option[OneOrSeq[Color]] = None, + font: Option[HoverLabelFont] = None, + align: Option[OneOrSeq[Alignment]] = None, + namelength: Option[OneOrSeq[Int]] = None, + uirevision: Option[Element] = None +) diff --git a/core/shared/src/main/scala/plotly/element/HoverLabelFont.scala b/core/shared/src/main/scala/plotly/element/HoverLabelFont.scala new file mode 100644 index 00000000..7b839530 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/HoverLabelFont.scala @@ -0,0 +1,9 @@ +package plotly.element + +import dataclass.data + +@data(optionSetters = true) class HoverLabelFont( + family: Option[OneOrSeq[String]] = None, + size: Option[OneOrSeq[Double]] = None, + color: Option[OneOrSeq[Color]] = None +) diff --git a/core/shared/src/main/scala/plotly/element/HoverOn.scala b/core/shared/src/main/scala/plotly/element/HoverOn.scala new file mode 100644 index 00000000..6cbc68f4 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/HoverOn.scala @@ -0,0 +1,9 @@ +package plotly.element + +sealed abstract class HoverOn(val label: String) extends Product with Serializable + +object HoverOn { + case object Points extends HoverOn("points") + case object Fills extends HoverOn("fills") + case object PointsFill extends HoverOn("points+fills") +} diff --git a/core/shared/src/main/scala/plotly/element/Line.scala b/core/shared/src/main/scala/plotly/element/Line.scala index 7a72aaf7..05168d43 100644 --- a/core/shared/src/main/scala/plotly/element/Line.scala +++ b/core/shared/src/main/scala/plotly/element/Line.scala @@ -1,25 +1,28 @@ package plotly package element -import java.lang.{ Double => JDouble } +import dataclass.data -final case class Line( - shape: Option[LineShape], - color: Option[OneOrSeq[Color]], - width: Option[OneOrSeq[Double]], - dash: Option[Dash], - outliercolor: Option[Color], - outlierwidth: Option[Double] +import java.lang.{Double => JDouble} + +@data(optionSetters = true) class Line( + shape: Option[LineShape] = None, + color: Option[OneOrSeq[Color]] = None, + width: Option[OneOrSeq[Double]] = None, + dash: Option[Dash] = None, + outliercolor: Option[Color] = None, + outlierwidth: Option[Double] = None ) object Line { + @deprecated("Use Line() and chain-call .with* methods on it instead", "0.8.0") def apply( - shape: LineShape = null, - color: OneOrSeq[Color] = null, - width: OneOrSeq[Double] = null, - dash: Dash = null, - outliercolor: Color = null, - outlierwidth: JDouble = null + shape: LineShape = null, + color: OneOrSeq[Color] = null, + width: OneOrSeq[Double] = null, + dash: Dash = null, + outliercolor: Color = null, + outlierwidth: JDouble = null ): Line = Line( Option(shape), @@ -27,6 +30,6 @@ object Line { Option(width), Option(dash), Option(outliercolor), - Option(outlierwidth) .map(x => x: Double) + Option(outlierwidth).map(x => x: Double) ) } diff --git a/core/shared/src/main/scala/plotly/element/LineShape.scala b/core/shared/src/main/scala/plotly/element/LineShape.scala index 70637e52..d9a98618 100644 --- a/core/shared/src/main/scala/plotly/element/LineShape.scala +++ b/core/shared/src/main/scala/plotly/element/LineShape.scala @@ -6,8 +6,8 @@ sealed abstract class LineShape(val label: String) extends Product with Serializ object LineShape { case object Linear extends LineShape("linear") case object Spline extends LineShape("spline") - case object VHV extends LineShape("vhv") - case object HVH extends LineShape("hvh") - case object VH extends LineShape("vh") - case object HV extends LineShape("hv") + case object VHV extends LineShape("vhv") + case object HVH extends LineShape("hvh") + case object VH extends LineShape("vh") + case object HV extends LineShape("hv") } diff --git a/core/shared/src/main/scala/plotly/element/LocalDateTime.scala b/core/shared/src/main/scala/plotly/element/LocalDateTime.scala index 8b0709ae..e140fa5a 100644 --- a/core/shared/src/main/scala/plotly/element/LocalDateTime.scala +++ b/core/shared/src/main/scala/plotly/element/LocalDateTime.scala @@ -1,25 +1,26 @@ package plotly.element +import dataclass.data + import scala.util.Try -case class LocalDateTime( - year: Int, - month: Int, - dayOfMonth: Int, - hour: Int, - minute: Int, - second: Int +@data class LocalDateTime( + year: Int, + month: Int, + dayOfMonth: Int, + hour: Int, + minute: Int, + second: Int ) { override def toString: String = f"$year-$month%02d-$dayOfMonth%02d $hour%02d:$minute%02d:$second%02d" } -object LocalDateTime { +object LocalDateTime extends PlotlyJavaTimeConversions { private object IntStr { def unapply(s: String): Option[Int] = - Try(s.toInt) - .toOption + Try(s.toInt).toOption } def parse(s: String): Option[LocalDateTime] = @@ -28,6 +29,8 @@ object LocalDateTime { (d.split('-'), t.split(':')) match { case (Array(IntStr(y), IntStr(m), IntStr(d)), Array(IntStr(h), IntStr(min), IntStr(s))) => Some(LocalDateTime(y, m, d, h, min, s)) + case (Array(IntStr(y), IntStr(m), IntStr(d)), Array(IntStr(h), IntStr(min))) => + Some(LocalDateTime(y, m, d, h, min, 0)) case _ => None } case _ => None diff --git a/core/shared/src/main/scala/plotly/element/Marker.scala b/core/shared/src/main/scala/plotly/element/Marker.scala index d30825cc..5efb90b9 100644 --- a/core/shared/src/main/scala/plotly/element/Marker.scala +++ b/core/shared/src/main/scala/plotly/element/Marker.scala @@ -1,31 +1,34 @@ package plotly package element -import java.lang.{ Double => JDouble } +import java.lang.{Double => JDouble} -final case class Marker( - size: Option[OneOrSeq[Int]], - color: Option[OneOrSeq[Color]], - opacity: Option[OneOrSeq[Double]], - line: Option[Line], - symbol: Option[OneOrSeq[Symbol]], - outliercolor: Option[Color], - sizeref: Option[Double], - sizemode: Option[SizeMode], - width: Option[OneOrSeq[Int]] +import dataclass.data + +@data(optionSetters = true) class Marker( + size: Option[OneOrSeq[Int]] = None, + color: Option[OneOrSeq[Color]] = None, + opacity: Option[OneOrSeq[Double]] = None, + line: Option[Line] = None, + symbol: Option[OneOrSeq[Symbol]] = None, + outliercolor: Option[Color] = None, + sizeref: Option[Double] = None, + sizemode: Option[SizeMode] = None, + width: Option[OneOrSeq[Int]] = None ) object Marker { + @deprecated("Use Marker() and chain-call .with* methods on it instead", "0.8.0") def apply( - size: OneOrSeq[Int] = null, - color: OneOrSeq[Color] = null, - opacity: OneOrSeq[Double] = null, - line: Line = null, - symbol: OneOrSeq[Symbol] = null, - outliercolor: Color = null, - sizeref: JDouble = null, - sizemode: SizeMode = null, - width: OneOrSeq[Int] = null + size: OneOrSeq[Int] = null, + color: OneOrSeq[Color] = null, + opacity: OneOrSeq[Double] = null, + line: Line = null, + symbol: OneOrSeq[Symbol] = null, + outliercolor: Color = null, + sizeref: JDouble = null, + sizemode: SizeMode = null, + width: OneOrSeq[Int] = null ): Marker = Marker( Option(size), diff --git a/core/shared/src/main/scala/plotly/element/OneOrSeq.scala b/core/shared/src/main/scala/plotly/element/OneOrSeq.scala index fb3582eb..c4c32a05 100644 --- a/core/shared/src/main/scala/plotly/element/OneOrSeq.scala +++ b/core/shared/src/main/scala/plotly/element/OneOrSeq.scala @@ -1,9 +1,11 @@ package plotly.element +import scala.language.implicitConversions + sealed abstract class OneOrSeq[T] extends Product with Serializable object OneOrSeq { - case class One[T](value: T) extends OneOrSeq[T] + case class One[T](value: T) extends OneOrSeq[T] case class Sequence[T](seq: Seq[T]) extends OneOrSeq[T] implicit def fromOne[T](value: T): OneOrSeq[T] = diff --git a/core/shared/src/main/scala/plotly/element/ScatterMode.scala b/core/shared/src/main/scala/plotly/element/ScatterMode.scala index 93d5b3e6..0747e800 100644 --- a/core/shared/src/main/scala/plotly/element/ScatterMode.scala +++ b/core/shared/src/main/scala/plotly/element/ScatterMode.scala @@ -1,7 +1,9 @@ package plotly package element -case class ScatterMode(flags: Set[ScatterMode.Flag]) +import dataclass.data + +@data class ScatterMode(flags: Set[ScatterMode.Flag]) object ScatterMode { def apply(flags: Flag*): ScatterMode = @@ -10,9 +12,9 @@ object ScatterMode { sealed abstract class Flag(val label: String) extends Product with Serializable case object Markers extends Flag("markers") - case object Text extends Flag("text") - case object Lines extends Flag("lines") + case object Text extends Flag("text") + case object Lines extends Flag("lines") - val flags = Seq(Markers, Text, Lines) + val flags = Seq(Markers, Text, Lines) val flagMap = flags.map(m => m.label -> m).toMap } diff --git a/core/shared/src/main/scala/plotly/element/Side.scala b/core/shared/src/main/scala/plotly/element/Side.scala index cd37d3ba..0e0137ac 100644 --- a/core/shared/src/main/scala/plotly/element/Side.scala +++ b/core/shared/src/main/scala/plotly/element/Side.scala @@ -4,8 +4,8 @@ package element sealed abstract class Side(val label: String) extends Product with Serializable object Side { - case object Left extends Side("left") - case object Right extends Side("right") - case object Top extends Side("top") + case object Left extends Side("left") + case object Right extends Side("right") + case object Top extends Side("top") case object Bottom extends Side("bottom") } diff --git a/core/shared/src/main/scala/plotly/element/SizeMode.scala b/core/shared/src/main/scala/plotly/element/SizeMode.scala index 578e5971..7a453ff6 100644 --- a/core/shared/src/main/scala/plotly/element/SizeMode.scala +++ b/core/shared/src/main/scala/plotly/element/SizeMode.scala @@ -4,5 +4,5 @@ sealed abstract class SizeMode(val label: String) extends Product with Serializa object SizeMode { case object Diameter extends SizeMode("diameter") - case object Area extends SizeMode("area") + case object Area extends SizeMode("area") } diff --git a/core/shared/src/main/scala/plotly/element/Symbol.scala b/core/shared/src/main/scala/plotly/element/Symbol.scala index 14ae03d9..ede2a32b 100644 --- a/core/shared/src/main/scala/plotly/element/Symbol.scala +++ b/core/shared/src/main/scala/plotly/element/Symbol.scala @@ -1,23 +1,24 @@ package plotly package element +import dataclass.data sealed abstract class Symbol(label0: String, idx0: Int) extends Product with Serializable { - def idx: Int = idx0 + (if (open) 100 else 0) - def label: String = label0 + (if (open) "-open" else "") + def idx: Int = idx0 + (if (open) 100 else 0) + def label: String = label0 + (if (open) "-open" else "") def open: Boolean } object Symbol { sealed abstract class DotSymbol(label0: String, idx0: Int) extends Symbol(label0, idx0) { - override def idx: Int = super.idx + (if (dot) 200 else 0) + override def idx: Int = super.idx + (if (dot) 200 else 0) override def label: String = super.label + (if (dot) "-dot" else "") def dot: Boolean } - case class Circle(open: Boolean = false, dot: Boolean = false) extends DotSymbol("circle", 0) - case class Square(open: Boolean = false, dot: Boolean = false) extends DotSymbol("square", 1) - case class Diamond(open: Boolean = false, dot: Boolean = false) extends DotSymbol("diamond", 2) - case class Cross(open: Boolean = false, dot: Boolean = false) extends DotSymbol("cross", 3) + @data class Circle(open: Boolean = false, dot: Boolean = false) extends DotSymbol("circle", 0) + @data class Square(open: Boolean = false, dot: Boolean = false) extends DotSymbol("square", 1) + @data class Diamond(open: Boolean = false, dot: Boolean = false) extends DotSymbol("diamond", 2) + @data class Cross(open: Boolean = false, dot: Boolean = false) extends DotSymbol("cross", 3) /* "4" | "x" | "104" | "x-open" | "204" | "x-dot" | "304" | "x-open-dot" @@ -61,5 +62,5 @@ object Symbol { "42" | "line-ns" | "142" | "line-ns-open" "43" | "line-ne" | "143" | "line-ne-open" "44" | "line-nw" | "144" | "line-nw-open - */ + */ } diff --git a/core/shared/src/main/scala/plotly/element/TextFont.scala b/core/shared/src/main/scala/plotly/element/TextFont.scala index 13f16697..ac2a9fbc 100644 --- a/core/shared/src/main/scala/plotly/element/TextFont.scala +++ b/core/shared/src/main/scala/plotly/element/TextFont.scala @@ -1,4 +1,6 @@ package plotly package element -final case class TextFont(family: String) +import dataclass.data + +@data class TextFont(family: String) diff --git a/core/shared/src/main/scala/plotly/element/TickMode.scala b/core/shared/src/main/scala/plotly/element/TickMode.scala new file mode 100644 index 00000000..2cfb97d1 --- /dev/null +++ b/core/shared/src/main/scala/plotly/element/TickMode.scala @@ -0,0 +1,9 @@ +package plotly.element + +sealed abstract class TickMode(val mode: String) extends Product with Serializable + +object TickMode { + case object Auto extends TickMode("auto") + case object Linear extends TickMode("linear") + case object Array extends TickMode("array") +} diff --git a/core/shared/src/main/scala/plotly/element/Ticks.scala b/core/shared/src/main/scala/plotly/element/Ticks.scala old mode 100644 new mode 100755 index 9142bd72..bd2256a5 --- a/core/shared/src/main/scala/plotly/element/Ticks.scala +++ b/core/shared/src/main/scala/plotly/element/Ticks.scala @@ -5,4 +5,6 @@ sealed abstract class Ticks(val label: String) extends Product with Serializable object Ticks { case object Outside extends Ticks("outside") + case object Inside extends Ticks("inside") + case object Empty extends Ticks("") } diff --git a/core/shared/src/main/scala/plotly/layout/Annotation.scala b/core/shared/src/main/scala/plotly/layout/Annotation.scala index b8a1dae3..a12d873b 100644 --- a/core/shared/src/main/scala/plotly/layout/Annotation.scala +++ b/core/shared/src/main/scala/plotly/layout/Annotation.scala @@ -1,33 +1,38 @@ package plotly package layout -import java.lang.{ Boolean => JBoolean } +import java.lang.{Boolean => JBoolean, Double => JDouble} +import dataclass.data import plotly.element._ -final case class Annotation( - xref: Option[Ref], - yref: Option[Ref], - x: Option[Element], - y: Option[Element], - xanchor: Option[Anchor], - yanchor: Option[Anchor], - text: Option[Element], - font: Option[Font], - showarrow: Option[Boolean] +@data(optionSetters = true) class Annotation( + xref: Option[Ref] = None, + yref: Option[Ref] = None, + x: Option[Element] = None, + y: Option[Element] = None, + xanchor: Option[Anchor] = None, + yanchor: Option[Anchor] = None, + text: Option[Element] = None, + font: Option[Font] = None, + showarrow: Option[Boolean] = None, + @since("0.8.0") + ax: Option[Double] = None, + ay: Option[Double] = None ) object Annotation { + @deprecated("Use Annotation() and chain-call .with* methods on it instead", "0.8.0") def apply( - xref: Ref = null, - yref: Ref = null, - x: Element = null, - y: Element = null, - xanchor: Anchor = null, - yanchor: Anchor = null, - text: Element = null, - font: Font = null, - showarrow: JBoolean = null + xref: Ref = null, + yref: Ref = null, + x: Element = null, + y: Element = null, + xanchor: Anchor = null, + yanchor: Anchor = null, + text: Element = null, + font: Font = null, + showarrow: JBoolean = null ): Annotation = Annotation( Option(xref), diff --git a/core/shared/src/main/scala/plotly/layout/Axis.scala b/core/shared/src/main/scala/plotly/layout/Axis.scala old mode 100644 new mode 100755 index a96dc5a5..b6c0bf2c --- a/core/shared/src/main/scala/plotly/layout/Axis.scala +++ b/core/shared/src/main/scala/plotly/layout/Axis.scala @@ -1,100 +1,132 @@ package plotly package layout -import java.lang.{ Integer => JInt, Double => JDouble, Boolean => JBoolean } +import java.lang.{Boolean => JBoolean, Double => JDouble, Integer => JInt} +import dataclass.data import plotly.element._ -final case class Axis( - title: Option[String], - titlefont: Option[Font], - showgrid: Option[Boolean], - gridwidth: Option[Int], - gridcolor: Option[Color], - showline: Option[Boolean], - showticklabels: Option[Boolean], - linecolor: Option[Color], - linewidth: Option[Int], - autotick: Option[Boolean], - tickcolor: Option[Color], - tickwidth: Option[Int], - tickangle: Option[Double], - dtick: Option[Double], - ticklen: Option[Int], - tickfont: Option[Font], - zeroline: Option[Boolean], - zerolinewidth: Option[Double], - zerolinecolor: Option[Color], - range: Option[(Double, Double)], - autorange: Option[Boolean], - ticks: Option[Ticks], - domain: Option[(Double, Double)], - side: Option[Side], - anchor: Option[AxisAnchor], - `type`: Option[AxisType], - overlaying: Option[AxisAnchor], - position: Option[Double] +@data(optionSetters = true) class Axis( + title: Option[String] = None, + titlefont: Option[Font] = None, + showgrid: Option[Boolean] = None, + gridwidth: Option[Int] = None, + gridcolor: Option[Color] = None, + showline: Option[Boolean] = None, + showticklabels: Option[Boolean] = None, + linecolor: Option[Color] = None, + linewidth: Option[Int] = None, + autotick: Option[Boolean] = None, + tickcolor: Option[Color] = None, + tickwidth: Option[Int] = None, + tickangle: Option[Double] = None, + dtick: Option[Double] = None, + ticklen: Option[Int] = None, + tickfont: Option[Font] = None, + tickprefix: Option[String] = None, + ticksuffix: Option[String] = None, + zeroline: Option[Boolean] = None, + zerolinewidth: Option[Double] = None, + zerolinecolor: Option[Color] = None, + range: Option[Range] = None, + autorange: Option[Boolean] = None, + ticks: Option[Ticks] = None, + domain: Option[Range] = None, + side: Option[Side] = None, + anchor: Option[AxisAnchor] = None, + `type`: Option[AxisType] = None, + overlaying: Option[AxisAnchor] = None, + position: Option[Double] = None, + tickmode: Option[TickMode] = None, + tickvals: Option[Sequence] = None, + ticktext: Option[Sequence] = None, + nticks: Option[Int] = None, + automargin: Option[Boolean] = None, + @since("0.8.0") + rangeslider: Option[RangeSlider] = None, + @since("0.8.2") + width: Option[Int] = None, + height: Option[Int] = None, + autosize: Option[Boolean] = None, + @since("0.8.5") + tickformat: Option[String] = None, + fixedrange: Option[Boolean] = None ) object Axis { + @deprecated("Use Axis() and chain-call .with* methods on it instead", "0.8.0") def apply( - title: String = null, - titlefont: Font = null, - showgrid: JBoolean = null, - gridwidth: JInt = null, - gridcolor: Color = null, - showline: JBoolean = null, - showticklabels: JBoolean = null, - linecolor: Color = null, - linewidth: JInt = null, - autotick: JBoolean = null, - tickcolor: Color = null, - tickwidth: JInt = null, - tickangle: JDouble = null, - dtick: JDouble = null, - ticklen: JInt = null, - tickfont: Font = null, - zeroline: JBoolean = null, - zerolinewidth: JDouble = null, - zerolinecolor: Color = null, - range: (Double, Double) = null, - autorange: JBoolean = null, - ticks: Ticks = null, - domain: (Double, Double) = null, - side: Side = null, - anchor: AxisAnchor = null, - `type`: AxisType = null, - overlaying: AxisAnchor = null, - position: JDouble = null + title: String = null, + titlefont: Font = null, + showgrid: JBoolean = null, + gridwidth: JInt = null, + gridcolor: Color = null, + showline: JBoolean = null, + showticklabels: JBoolean = null, + linecolor: Color = null, + linewidth: JInt = null, + autotick: JBoolean = null, + tickcolor: Color = null, + tickwidth: JInt = null, + tickangle: JDouble = null, + dtick: JDouble = null, + ticklen: JInt = null, + tickfont: Font = null, + tickprefix: String = null, + ticksuffix: String = null, + zeroline: JBoolean = null, + zerolinewidth: JDouble = null, + zerolinecolor: Color = null, + range: (Double, Double) = null, + autorange: JBoolean = null, + ticks: Ticks = null, + domain: (Double, Double) = null, + side: Side = null, + anchor: AxisAnchor = null, + `type`: AxisType = null, + overlaying: AxisAnchor = null, + position: JDouble = null, + tickmode: TickMode = null, + tickvals: Sequence = null, + ticktext: Sequence = null, + nticks: JInt = null, + automargin: JBoolean = null ): Axis = Axis( Option(title), Option(titlefont), - Option(showgrid) .map(x => x: Boolean), - Option(gridwidth) .map(x => x: Int), + Option(showgrid).map(x => x: Boolean), + Option(gridwidth).map(x => x: Int), Option(gridcolor), - Option(showline) .map(x => x: Boolean), - Option(showticklabels) .map(x => x: Boolean), + Option(showline).map(x => x: Boolean), + Option(showticklabels).map(x => x: Boolean), Option(linecolor), - Option(linewidth) .map(x => x: Int), - Option(autotick) .map(x => x: Boolean), + Option(linewidth).map(x => x: Int), + Option(autotick).map(x => x: Boolean), Option(tickcolor), - Option(tickwidth) .map(x => x: Int), - Option(tickangle) .map(x => x: Double), - Option(dtick) .map(x => x: Double), - Option(ticklen) .map(x => x: Int), + Option(tickwidth).map(x => x: Int), + Option(tickangle).map(x => x: Double), + Option(dtick).map(x => x: Double), + Option(ticklen).map(x => x: Int), Option(tickfont), - Option(zeroline) .map(x => x: Boolean), - Option(zerolinewidth) .map(x => x: Double), + Option(tickprefix), + Option(ticksuffix), + Option(zeroline).map(x => x: Boolean), + Option(zerolinewidth).map(x => x: Double), Option(zerolinecolor), - Option(range), - Option(autorange) .map(x => x: Boolean), + Option(range).map(x => x: Range), + Option(autorange).map(x => x: Boolean), Option(ticks), - Option(domain), + Option(domain).map(x => x: Range), Option(side), Option(anchor), Option(`type`), Option(overlaying), - Option(position) .map(x => x: Double) + Option(position).map(x => x: Double), + Option(tickmode), + Option(tickvals), + Option(ticktext), + Option(nticks).map(x => x: Int), + Option(automargin).map(x => x: Boolean) ) } diff --git a/core/shared/src/main/scala/plotly/layout/BarMode.scala b/core/shared/src/main/scala/plotly/layout/BarMode.scala index 202a7e37..1d58c827 100644 --- a/core/shared/src/main/scala/plotly/layout/BarMode.scala +++ b/core/shared/src/main/scala/plotly/layout/BarMode.scala @@ -4,7 +4,8 @@ package layout sealed abstract class BarMode(val label: String) extends Product with Serializable object BarMode { - case object Group extends BarMode("group") - case object Stack extends BarMode("stack") - case object Overlay extends BarMode("overlay") -} \ No newline at end of file + case object Group extends BarMode("group") + case object Stack extends BarMode("stack") + case object Overlay extends BarMode("overlay") + case object Relative extends BarMode("relative") +} diff --git a/core/shared/src/main/scala/plotly/layout/Font.scala b/core/shared/src/main/scala/plotly/layout/Font.scala index a632df2f..30789cd0 100644 --- a/core/shared/src/main/scala/plotly/layout/Font.scala +++ b/core/shared/src/main/scala/plotly/layout/Font.scala @@ -1,21 +1,36 @@ package plotly package layout -import plotly.element._ +import java.lang.{Integer => JInt} -import java.lang.{ Integer => JInt } +import dataclass.data +import plotly.element._ -final case class Font( - size: Option[Int], - family: Option[String], - color: Option[Color] +@data(optionSetters = true) class Font( + size: Option[Int] = None, + family: Option[String] = None, + color: Option[Color] = None ) object Font { + + def apply(size: Int, family: String, color: Color): Font = + Font(Some(size), Some(family), Some(color)) + + def apply(size: Int, family: String): Font = + Font(Some(size), Some(family), None) + + def apply(size: Int): Font = + Font(Some(size), None, None) + + def apply(color: Color): Font = + Font(None, None, Some(color)) + + @deprecated("Use Font() and chain-call .with* methods on it instead", "0.8.0") def apply( - size: JInt = null, - family: String = null, - color: Color = null + size: JInt = null, + family: String = null, + color: Color = null ): Font = Font( Option(size).map(x => x: Int), diff --git a/core/shared/src/main/scala/plotly/layout/Grid.scala b/core/shared/src/main/scala/plotly/layout/Grid.scala new file mode 100644 index 00000000..67c892b6 --- /dev/null +++ b/core/shared/src/main/scala/plotly/layout/Grid.scala @@ -0,0 +1,11 @@ +package plotly.layout + +import dataclass.data + +@data(optionSetters = true) class Grid( + rows: Option[Int] = None, + columns: Option[Int] = None, + pattern: Option[Pattern] = None, + roworder: Option[RowOrder] = None, + subplots: Option[Seq[Seq[String]]] = None +) diff --git a/core/shared/src/main/scala/plotly/layout/Layout.scala b/core/shared/src/main/scala/plotly/layout/Layout.scala index 5839c1f9..4dbcf707 100644 --- a/core/shared/src/main/scala/plotly/layout/Layout.scala +++ b/core/shared/src/main/scala/plotly/layout/Layout.scala @@ -1,67 +1,76 @@ package plotly package layout -import java.lang.{ Integer => JInt, Double => JDouble, Boolean => JBoolean } +import java.lang.{Boolean => JBoolean, Double => JDouble, Integer => JInt} +import dataclass.data import plotly.element._ -final case class Layout( - title: Option[String], - legend: Option[Legend], - width: Option[Int], - height: Option[Int], - showlegend: Option[Boolean], - xaxis: Option[Axis], - yaxis: Option[Axis], - xaxis1: Option[Axis], - xaxis2: Option[Axis], - xaxis3: Option[Axis], - xaxis4: Option[Axis], - yaxis1: Option[Axis], - yaxis2: Option[Axis], - yaxis3: Option[Axis], - yaxis4: Option[Axis], - barmode: Option[BarMode], - autosize: Option[Boolean], - margin: Option[Margin], - annotations: Option[Seq[Annotation]], - plot_bgcolor: Option[Color], - paper_bgcolor: Option[Color], - font: Option[Font], - bargap: Option[Double], - bargroupgap: Option[Double], - hovermode: Option[HoverMode], - boxmode: Option[BoxMode] +@data(optionSetters = true) class Layout( + title: Option[String] = None, + legend: Option[Legend] = None, + width: Option[Int] = None, + height: Option[Int] = None, + showlegend: Option[Boolean] = None, + xaxis: Option[Axis] = None, + yaxis: Option[Axis] = None, + xaxis1: Option[Axis] = None, + xaxis2: Option[Axis] = None, + xaxis3: Option[Axis] = None, + xaxis4: Option[Axis] = None, + yaxis1: Option[Axis] = None, + yaxis2: Option[Axis] = None, + yaxis3: Option[Axis] = None, + yaxis4: Option[Axis] = None, + barmode: Option[BarMode] = None, + autosize: Option[Boolean] = None, + margin: Option[Margin] = None, + annotations: Option[Seq[Annotation]] = None, + plot_bgcolor: Option[Color] = None, + paper_bgcolor: Option[Color] = None, + font: Option[Font] = None, + bargap: Option[Double] = None, + bargroupgap: Option[Double] = None, + hovermode: Option[HoverMode] = None, + boxmode: Option[BoxMode] = None, + scene: Option[Scene] = None, + @since("0.8.0") + dragmode: Option[String] = None, + shapes: Option[Seq[Shape]] = None, + @since("0.8.2") + grid: Option[Grid] = None ) object Layout { + @deprecated("Use Layout() and chain-call .with* methods on it instead", "0.8.0") def apply( - title: String = null, - legend: Legend = null, - width: JInt = null, - height: JInt = null, - showlegend: JBoolean = null, - xaxis: Axis = null, - yaxis: Axis = null, - xaxis1: Axis = null, - xaxis2: Axis = null, - xaxis3: Axis = null, - xaxis4: Axis = null, - yaxis1: Axis = null, - yaxis2: Axis = null, - yaxis3: Axis = null, - yaxis4: Axis = null, - barmode: BarMode = null, - autosize: JBoolean = null, - margin: Margin = null, + title: String = null, + legend: Legend = null, + width: JInt = null, + height: JInt = null, + showlegend: JBoolean = null, + xaxis: Axis = null, + yaxis: Axis = null, + xaxis1: Axis = null, + xaxis2: Axis = null, + xaxis3: Axis = null, + xaxis4: Axis = null, + yaxis1: Axis = null, + yaxis2: Axis = null, + yaxis3: Axis = null, + yaxis4: Axis = null, + barmode: BarMode = null, + autosize: JBoolean = null, + margin: Margin = null, annotations: Seq[Annotation] = null, - plot_bgcolor: Color = null, - paper_bgcolor: Color = null, - font: Font = null, - bargap: JDouble = null, - bargroupgap: JDouble = null, - hovermode: HoverMode = null, - boxmode: BoxMode = null + plot_bgcolor: Color = null, + paper_bgcolor: Color = null, + font: Font = null, + bargap: JDouble = null, + bargroupgap: JDouble = null, + hovermode: HoverMode = null, + boxmode: BoxMode = null, + scene: Scene = null ): Layout = new Layout( Option(title), @@ -89,6 +98,7 @@ object Layout { Option(bargap).map(x => x), Option(bargroupgap).map(x => x), Option(hovermode), - Option(boxmode) + Option(boxmode), + Option(scene) ) } diff --git a/core/shared/src/main/scala/plotly/layout/Legend.scala b/core/shared/src/main/scala/plotly/layout/Legend.scala index 2db2163a..daac773f 100644 --- a/core/shared/src/main/scala/plotly/layout/Legend.scala +++ b/core/shared/src/main/scala/plotly/layout/Legend.scala @@ -1,33 +1,38 @@ package plotly package layout -import java.lang.{ Double => JDouble } +import java.lang.{Double => JDouble} +import dataclass._ import plotly.element._ -final case class Legend( - x: Option[Double], - y: Option[Double], - traceorder: Option[TraceOrder], - yref: Option[Ref], - font: Option[Font], - bordercolor: Option[Color], - bgcolor: Option[Color], - xanchor: Option[Anchor], - yanchor: Option[Anchor] +@data(optionSetters = true) class Legend( + x: Option[Double] = None, + y: Option[Double] = None, + traceorder: Option[TraceOrder] = None, + yref: Option[Ref] = None, + font: Option[Font] = None, + bordercolor: Option[Color] = None, + bgcolor: Option[Color] = None, + xanchor: Option[Anchor] = None, + yanchor: Option[Anchor] = None, + @since + orientation: Option[Orientation] = None ) object Legend { + @deprecated("Use Legend() and chain-call .with* methods on it instead", "0.8.0") def apply( - x: JDouble = null, - y: JDouble = null, - traceorder: TraceOrder = null, - yref: Ref = null, - font: Font = null, - bordercolor: Color = null, - bgcolor: Color = null, - xanchor: Anchor = null, - yanchor: Anchor = null + x: JDouble = null, + y: JDouble = null, + traceorder: TraceOrder = null, + yref: Ref = null, + font: Font = null, + bordercolor: Color = null, + bgcolor: Color = null, + xanchor: Anchor = null, + yanchor: Anchor = null, + orientation: Orientation = null ): Legend = Legend( Option(x).map(v => v: Double), @@ -38,6 +43,7 @@ object Legend { Option(bordercolor), Option(bgcolor), Option(xanchor), - Option(yanchor) + Option(yanchor), + Option(orientation) ) -} \ No newline at end of file +} diff --git a/core/shared/src/main/scala/plotly/layout/Margin.scala b/core/shared/src/main/scala/plotly/layout/Margin.scala index 76730d51..386e4aa3 100644 --- a/core/shared/src/main/scala/plotly/layout/Margin.scala +++ b/core/shared/src/main/scala/plotly/layout/Margin.scala @@ -1,29 +1,37 @@ package plotly package layout -import java.lang.{ Integer => JInt, Boolean => JBoolean } +import java.lang.{Boolean => JBoolean, Integer => JInt} +import dataclass.data -final case class Margin( - autoexpand: Option[Boolean], - l: Option[Int], - r: Option[Int], - t: Option[Int], - b: Option[Int] +@data(optionSetters = true) class Margin( + autoexpand: Option[Boolean] = None, + l: Option[Int] = None, + r: Option[Int] = None, + t: Option[Int] = None, + b: Option[Int] = None, + pad: Option[Int] = None ) object Margin { + def apply(l: Int, r: Int, t: Int, b: Int): Margin = + Margin().withL(l).withR(r).withT(t).withB(b) + + @deprecated("Use Margin() and chain-call .with* methods on it instead", "0.8.0") def apply( - autoexpand: JBoolean = null, - l: JInt = null, - r: JInt = null, - t: JInt = null, - b: JInt = null + autoexpand: JBoolean = null, + l: JInt = null, + r: JInt = null, + t: JInt = null, + b: JInt = null, + pad: JInt = null ): Margin = Margin( Option(autoexpand).map(b => b: Boolean), Option(l).map(n => n: Int), Option(r).map(n => n: Int), Option(t).map(n => n: Int), - Option(b).map(n => n: Int) + Option(b).map(n => n: Int), + Option(pad).map(n => n: Int) ) } diff --git a/core/shared/src/main/scala/plotly/layout/Pattern.scala b/core/shared/src/main/scala/plotly/layout/Pattern.scala new file mode 100644 index 00000000..f778371a --- /dev/null +++ b/core/shared/src/main/scala/plotly/layout/Pattern.scala @@ -0,0 +1,8 @@ +package plotly.layout + +sealed abstract class Pattern(val label: String) extends Product with Serializable + +object Pattern { + case object Independent extends Pattern("independent") + case object Coupled extends Pattern("coupled") +} diff --git a/core/shared/src/main/scala/plotly/layout/RangeSlider.scala b/core/shared/src/main/scala/plotly/layout/RangeSlider.scala new file mode 100644 index 00000000..db3af2c9 --- /dev/null +++ b/core/shared/src/main/scala/plotly/layout/RangeSlider.scala @@ -0,0 +1,8 @@ +package plotly.layout + +import dataclass.data +import plotly.Range + +@data(optionSetters = true) class RangeSlider( + range: Option[Range] = None +) diff --git a/core/shared/src/main/scala/plotly/layout/Ref.scala b/core/shared/src/main/scala/plotly/layout/Ref.scala index bbb30ebf..382dbd9d 100644 --- a/core/shared/src/main/scala/plotly/layout/Ref.scala +++ b/core/shared/src/main/scala/plotly/layout/Ref.scala @@ -6,6 +6,6 @@ import plotly.element._ sealed abstract class Ref(val label: String) extends Product with Serializable object Ref { - case object Paper extends Ref("paper") + case object Paper extends Ref("paper") case class Axis(underlying: AxisReference) extends Ref(underlying.label) } diff --git a/core/shared/src/main/scala/plotly/layout/RowOrder.scala b/core/shared/src/main/scala/plotly/layout/RowOrder.scala new file mode 100644 index 00000000..9a3b5b1d --- /dev/null +++ b/core/shared/src/main/scala/plotly/layout/RowOrder.scala @@ -0,0 +1,8 @@ +package plotly.layout + +sealed abstract class RowOrder(val label: String) extends Product with Serializable + +object RowOrder { + case object TopToBottom extends RowOrder("top to bottom") + case object BottomToTop extends RowOrder("bottom to top") +} diff --git a/core/shared/src/main/scala/plotly/layout/Scene.scala b/core/shared/src/main/scala/plotly/layout/Scene.scala new file mode 100644 index 00000000..52ab727d --- /dev/null +++ b/core/shared/src/main/scala/plotly/layout/Scene.scala @@ -0,0 +1,26 @@ +package plotly +package layout + +import java.lang.{Boolean => JBoolean, Double => JDouble, Integer => JInt} + +import dataclass.data +import plotly.element._ + +@data(optionSetters = true) class Scene( + xaxis: Option[Axis] = None, + yaxis: Option[Axis] = None, + zaxis: Option[Axis] = None +) + +object Scene { + @deprecated("Use Scene() and chain-call .with* methods on it instead", "0.8.0") + def apply( + xaxis: Axis = null, + yaxis: Axis = null, + zaxis: Axis = null + ): Scene = new Scene( + Option(xaxis), + Option(yaxis), + Option(zaxis) + ) +} diff --git a/core/shared/src/main/scala/plotly/layout/Shape.scala b/core/shared/src/main/scala/plotly/layout/Shape.scala new file mode 100644 index 00000000..c9b4bd2b --- /dev/null +++ b/core/shared/src/main/scala/plotly/layout/Shape.scala @@ -0,0 +1,17 @@ +package plotly.layout + +import dataclass.data +import plotly.element.{Color, Line} + +@data(optionSetters = true) class Shape( + `type`: Option[String] = None, + xref: Option[String] = None, + yref: Option[String] = None, + x0: Option[String] = None, + y0: Option[Double] = None, + x1: Option[String] = None, + y1: Option[Double] = None, + fillcolor: Option[Color] = None, + opacity: Option[Double] = None, + line: Option[Line] = None +) diff --git a/demo/src/main/resources/index.html b/demo/src/main/resources/index.html index 47aaa5ca..1f7b03d9 100644 --- a/demo/src/main/resources/index.html +++ b/demo/src/main/resources/index.html @@ -32,7 +32,7 @@ | | |diff --git a/demo/src/main/scala/plotly/demo/Demo.scala b/demo/src/main/scala/plotly/demo/Demo.scala old mode 100644 new mode 100755 index c2da9e63..dbca5966 --- a/demo/src/main/scala/plotly/demo/Demo.scala +++ b/demo/src/main/scala/plotly/demo/Demo.scala @@ -1,14 +1,14 @@ package plotly.demo -import scala.scalajs.js.annotation.JSExport +import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel} import plotly.Plotly import org.scalajs.dom -import scalatags.JsDom.all._ +import scalatags.JsDom.all.{area => _, _} -@JSExport object Demo { +@JSExportTopLevel("Demo") object Demo { val demos = Seq( "Line Charts" -> Seq( @@ -37,20 +37,31 @@ import scalatags.JsDom.all._ ), "Filled Area Plots" -> Seq( area.BasicOverlaidAreaChart + ), + "Heatmaps" -> Seq( + heatmaps.BasicHeatmap, + heatmaps.CategoricalAxisHeatmap, + heatmaps.CustomColorScaleHeatmap, + heatmaps.AnnotatedHeatmap + ), + "Histogram" -> Seq( + histogram.BasicHistogram, + histogram.StyledBasicHistogram ) ) def unindent(source: String): String = { - val lines = source.lines.toVector - val nonEmptyLines = lines.filter(_.exists(!_.isSpaceChar)) + val lines = source.linesIterator.toVector + val nonEmptyLines: Vector[String] = lines.filter(_.exists(!_.isSpaceChar)) if (nonEmptyLines.isEmpty) source else { - val dropCount = Stream.from(0) - .takeWhile(idx => nonEmptyLines.forall(_(idx) == nonEmptyLines.head(idx))) + val dropCount = LazyList + .from(0) + .takeWhile(idx => nonEmptyLines.forall(str => str(idx) == nonEmptyLines.head(idx))) .lastOption .fold(0)(_ + 1) @@ -76,10 +87,9 @@ import scalatags.JsDom.all._ h2("Examples"), div( style := "margin-left: 3em;", - demos.map { - case (chartType, chartDemos) => - val chartTypeId0 = chartTypeId(chartType) - a(href := "#" + chartTypeId0, h3(chartType)) + demos.map { case (chartType, chartDemos) => + val chartTypeId0 = chartTypeId(chartType) + a(href := "#" + chartTypeId0, h3(chartType)) } ) ) @@ -91,37 +101,34 @@ import scalatags.JsDom.all._ val chartTypeId0 = chartTypeId(chartType) - val chartTypeElem = h2(id := chartTypeId0, - a(href := "#" + chartTypeId0, - chartType - ) - ) + val chartTypeElem = h2(id := chartTypeId0, a(href := "#" + chartTypeId0, chartType)) mainDiv.appendChild(chartTypeElem.render) for (demo <- chartDemos) { - Console.println(s"Rendering demo ${demo.id}") + Console.println(s" Rendering demo ${demo.id}") val divId = s"demo-${demo.id}" val elem = - div(id := demo.id, `class` := "panel panel-default", - div(`class` := "panel-heading", - a(href := "#" + demo.id, - h4(demo.id) - ) - ), - div(`class` := "panel-body", - div(`class` := "example-code", + div( + id := demo.id, + `class` := "panel panel-default", + div(`class` := "panel-heading", a(href := "#" + demo.id, h4(demo.id))), + div( + `class` := "panel-body", + div( + `class` := "example-code", pre( - code(`class` := "language-scala", - s"""import plotly._ - |import plotly.element._${if (demo.layout == null) "" else "\nimport plotly.layout._"} + code( + `class` := "language-scala", + s"""import plotly._ + |import plotly.element._${if (demo.layout == null) "" else "\nimport plotly.layout._"} """.stripMargin + - unindent(demo.source) + - s""" - | - |Plotly.plot("div-id", data${if (demo.layout == null) "" else ", layout"})""".stripMargin + unindent(demo.source) + + s""" + | + |Plotly.plot("div-id", data${if (demo.layout == null) "" else ", layout"})""".stripMargin ) ) ), diff --git a/demo/src/main/scala/plotly/demo/area/BasicOverlaidAreaChart.scala b/demo/src/main/scala/plotly/demo/area/BasicOverlaidAreaChart.scala index c4fa3adc..b458edca 100644 --- a/demo/src/main/scala/plotly/demo/area/BasicOverlaidAreaChart.scala +++ b/demo/src/main/scala/plotly/demo/area/BasicOverlaidAreaChart.scala @@ -7,22 +7,16 @@ import plotly.element.Fill object BasicOverlaidAreaChart extends NoLayoutDemoChart { def plotlyDocUrl = "https://plot.ly/javascript/filled-area-plots/#basic-overlaid-area-chart" - def id = "basic-overlaid-area-chart" - def source = BasicOverlaidAreaChartSource.source + def id = "basic-overlaid-area-chart" + def source = BasicOverlaidAreaChartSource.source // demo source start - - val trace1 = Scatter( - Seq(1, 2, 3, 4), - Seq(0, 2, 3, 5), - fill = Fill.ToZeroY - ) - - val trace2 = Scatter( - Seq(1, 2, 3, 4), - Seq(3, 5, 1, 7), - fill = Fill.ToNextY - ) + + val trace1 = Scatter(Seq(1, 2, 3, 4), Seq(0, 2, 3, 5)) + .withFill(Fill.ToZeroY) + + val trace2 = Scatter(Seq(1, 2, 3, 4), Seq(3, 5, 1, 7)) + .withFill(Fill.ToNextY) val data = Seq(trace1, trace2) diff --git a/demo/src/main/scala/plotly/demo/bar/BarChartWithDirectLabels.scala b/demo/src/main/scala/plotly/demo/bar/BarChartWithDirectLabels.scala index a68ac204..c80a38ea 100644 --- a/demo/src/main/scala/plotly/demo/bar/BarChartWithDirectLabels.scala +++ b/demo/src/main/scala/plotly/demo/bar/BarChartWithDirectLabels.scala @@ -8,8 +8,8 @@ import plotly.layout._ object BarChartWithDirectLabels extends DemoChart { def plotlyDocUrl = "https://plot.ly/javascript/bar-charts/#bar-chart-with-direct-labels" - def id = "bar-chart-with-direct-labels" - def source = BarChartWithDirectLabelsSource.source + def id = "bar-chart-with-direct-labels" + def source = BarChartWithDirectLabelsSource.source // demo source start @@ -17,38 +17,34 @@ object BarChartWithDirectLabels extends DemoChart { val yValue = Seq(20, 14, 23) - val trace1 = Bar( - xValue, - yValue, - text = Seq("27% market share", "24% market share", "19% market share"), - marker = Marker( - color = Color.RGB(158, 202, 225), - opacity = 0.6, - line = Line( - color = Color.RGB(8, 48, 107), - width = 1.5 - ) + val trace1 = Bar(xValue, yValue) + .withText(Seq("27% market share", "24% market share", "19% market share")) + .withMarker( + Marker() + .withColor(Color.RGB(158, 202, 225)) + .withOpacity(0.6) + .withLine( + Line() + .withColor(Color.RGB(8, 48, 107)) + .withWidth(1.5) + ) ) - ) val data = Seq(trace1) - val annotations = xValue.zip(yValue).map { - case (x, y) => - Annotation( - x = x, - y = y, - text = y.toString, - xanchor = Anchor.Center, - yanchor = Anchor.Bottom, - showarrow = false - ) + val annotations = xValue.zip(yValue).map { case (x, y) => + Annotation() + .withX(x) + .withY(y) + .withText(y.toString) + .withXanchor(Anchor.Center) + .withYanchor(Anchor.Bottom) + .withShowarrow(false) } - val layout = Layout( - title = "January 2013 Sales Report", - annotations = annotations - ) + val layout = Layout() + .withTitle("January 2013 Sales Report") + .withAnnotations(annotations) // demo source end diff --git a/demo/src/main/scala/plotly/demo/bar/BasicBarChart.scala b/demo/src/main/scala/plotly/demo/bar/BasicBarChart.scala index 1d1e29a7..99b69eaf 100644 --- a/demo/src/main/scala/plotly/demo/bar/BasicBarChart.scala +++ b/demo/src/main/scala/plotly/demo/bar/BasicBarChart.scala @@ -7,8 +7,8 @@ import plotly.element._ object BasicBarChart extends NoLayoutDemoChart { def plotlyDocUrl = "https://plot.ly/javascript/bar-charts/#basic-bar-chart" - def id = "basic-bar-chart" - def source = BasicBarChartSource.source + def id = "basic-bar-chart" + def source = BasicBarChartSource.source // demo source start diff --git a/demo/src/main/scala/plotly/demo/bar/CustomizingIndividualBarColors.scala b/demo/src/main/scala/plotly/demo/bar/CustomizingIndividualBarColors.scala index d33bc196..c4c61487 100644 --- a/demo/src/main/scala/plotly/demo/bar/CustomizingIndividualBarColors.scala +++ b/demo/src/main/scala/plotly/demo/bar/CustomizingIndividualBarColors.scala @@ -8,29 +8,35 @@ import plotly.layout._ object CustomizingIndividualBarColors extends DemoChart { def plotlyDocUrl = "" - def id = "customizing-individual-bar-colors" - def source = CustomizingIndividualBarColorsSource.source + def id = "customizing-individual-bar-colors" + def source = CustomizingIndividualBarColorsSource.source // demo source start - val defaultColor = Color.RGBA(204,204,204,1) - val highlightColor = Color.RGBA(222,45,38,0.8) + val defaultColor = Color.RGBA(204, 204, 204, 1) + val highlightColor = Color.RGBA(222, 45, 38, 0.8) val trace1 = Bar( Seq("Feature A", "Feature B", "Feature C", "Feature D", "Feature E"), - Seq(20, 14, 23, 25, 22), - marker = Marker( - color = Seq( - defaultColor, highlightColor, defaultColor, defaultColor, defaultColor - ) - ) + Seq(20, 14, 23, 25, 22) ) + .withMarker( + Marker() + .withColor( + Seq( + defaultColor, + highlightColor, + defaultColor, + defaultColor, + defaultColor + ) + ) + ) val data = Seq(trace1) - val layout = Layout( - title = "Least Used Feature" - ) + val layout = Layout() + .withTitle("Least Used Feature") // demo source end diff --git a/demo/src/main/scala/plotly/demo/bar/GroupedBarChart.scala b/demo/src/main/scala/plotly/demo/bar/GroupedBarChart.scala index d244d192..dca36376 100644 --- a/demo/src/main/scala/plotly/demo/bar/GroupedBarChart.scala +++ b/demo/src/main/scala/plotly/demo/bar/GroupedBarChart.scala @@ -8,28 +8,21 @@ import plotly.layout._ object GroupedBarChart extends DemoChart { def plotlyDocUrl = "https://plot.ly/javascript/bar-charts/#grouped-bar-chart" - def id = "grouped-bar-chart" - def source = GroupedBarChartSource.source + def id = "grouped-bar-chart" + def source = GroupedBarChartSource.source // demo source start - val trace1 = Bar( - Seq("giraffes", "orangutans", "monkeys"), - Seq(20, 14, 23), - name = "SF Zoo" - ) + val trace1 = Bar(Seq("giraffes", "orangutans", "monkeys"), Seq(20, 14, 23)) + .withName("SF Zoo") - val trace2 = Bar( - Seq("giraffes", "orangutans", "monkeys"), - Seq(12, 18, 29), - name = "LA Zoo" - ) + val trace2 = Bar(Seq("giraffes", "orangutans", "monkeys"), Seq(12, 18, 29)) + .withName("LA Zoo") val data = Seq(trace1, trace2) - val layout = Layout( - barmode = BarMode.Group - ) + val layout = Layout() + .withBarmode(BarMode.Group) // demo source end diff --git a/demo/src/main/scala/plotly/demo/bar/WaterfallBarChart.scala b/demo/src/main/scala/plotly/demo/bar/WaterfallBarChart.scala index f52d96b3..27c8c69f 100644 --- a/demo/src/main/scala/plotly/demo/bar/WaterfallBarChart.scala +++ b/demo/src/main/scala/plotly/demo/bar/WaterfallBarChart.scala @@ -8,8 +8,8 @@ import plotly.layout.{Annotation, BarMode, Font, Layout} object WaterfallBarChart extends DemoChart { def plotlyDocUrl = "https://plot.ly/javascript/bar-charts/#waterfall-bar-chart" - def id = "waterfall-bar-chart" - def source = WaterfallBarChartSource.source + def id = "waterfall-bar-chart" + def source = WaterfallBarChartSource.source // demo source start @@ -27,85 +27,79 @@ object WaterfallBarChart extends DemoChart { val textList = Seq("$430K", "$260K", "$690K", "$-120K", "$-200K", "$-320K", "$370K") - //Base + // Base - val trace1 = Bar( - x = xData, - y = Seq(0, 430, 0, 570, 370, 370, 0), - marker = Marker( - color = Color.RGBA(1, 1, 1, 0.0) + val trace1 = Bar(xData, Seq(0, 430, 0, 570, 370, 370, 0)) + .withMarker( + Marker() + .withColor(Color.RGBA(1, 1, 1, 0.0)) ) - ) - - //Revenue - val trace2 = Bar( - xData, - Seq(430, 260, 690, 0, 0, 0, 0), - marker = Marker( - color = Color.RGBA(55, 128, 191, 0.7), - line = Line( - color = Color.RGBA(55, 128, 191, 1.0), - width = 2.0 - ) + // Revenue + + val trace2 = Bar(xData, Seq(430, 260, 690, 0, 0, 0, 0)) + .withMarker( + Marker() + .withColor(Color.RGBA(55, 128, 191, 0.7)) + .withLine( + Line() + .withColor(Color.RGBA(55, 128, 191, 1.0)) + .withWidth(2.0) + ) ) - ) - - //Cost - val trace3 = Bar( - xData, - Seq(0, 0, 0, 120, 200, 320, 0), - marker = Marker( - color = Color.RGBA(219, 64, 82, 0.7), - line = Line( - color = Color.RGBA(219, 64, 82, 1.0), - width = 2.0 - ) + // Cost + + val trace3 = Bar(xData, Seq(0, 0, 0, 120, 200, 320, 0)) + .withMarker( + Marker() + .withColor(Color.RGBA(219, 64, 82, 0.7)) + .withLine( + Line() + .withColor(Color.RGBA(219, 64, 82, 1.0)) + .withWidth(2.0) + ) ) - ) - - //Profit - val trace4 = Bar( - xData, - Seq(0, 0, 0, 0, 0, 0, 370), - marker = Marker( - color = Color.RGBA(50,171, 96, 0.7), - line = Line( - color = Color.RGBA(50, 171, 96, 1.0), - width = 2.0 - ) + // Profit + + val trace4 = Bar(xData, Seq(0, 0, 0, 0, 0, 0, 370)) + .withMarker( + Marker() + .withColor(Color.RGBA(50, 171, 96, 0.7)) + .withLine( + Line() + .withColor(Color.RGBA(50, 171, 96, 1.0)) + .withWidth(2.0) + ) ) - ) val data = Seq(trace1, trace2, trace3, trace4) - val annotations = xData.zip(yData).zip(textList).map { - case ((x, y), text) => - Annotation( - x = x, - y = y, - text = text, - font = Font( + val annotations = xData.zip(yData).zip(textList).map { case ((x, y), text) => + Annotation() + .withX(x) + .withY(y) + .withText(text) + .withFont( + Font( family = "Arial", size = 14, color = Color.RGBA(245, 246, 249, 1) - ), - showarrow = false + ) ) + .withShowarrow(false) } - val layout = Layout( - title = "Annual Profit 2015", - barmode = BarMode.Stack, - paper_bgcolor = Color.RGBA(245, 246, 249, 1), - plot_bgcolor = Color.RGBA(245, 246, 249, 1), - width = 600, - height = 400, - showlegend = false, - annotations = annotations - ) + val layout = Layout() + .withTitle("Annual Profit 2015") + .withBarmode(BarMode.Stack) + .withPaper_bgcolor(Color.RGBA(245, 246, 249, 1)) + .withPlot_bgcolor(Color.RGBA(245, 246, 249, 1)) + .withWidth(600) + .withHeight(400) + .withShowlegend(false) + .withAnnotations(annotations) // demo source end diff --git a/demo/src/main/scala/plotly/demo/bubblecharts/HoverOnTextBubbleChart.scala b/demo/src/main/scala/plotly/demo/bubblecharts/HoverOnTextBubbleChart.scala index d2feb562..b12e665c 100644 --- a/demo/src/main/scala/plotly/demo/bubblecharts/HoverOnTextBubbleChart.scala +++ b/demo/src/main/scala/plotly/demo/bubblecharts/HoverOnTextBubbleChart.scala @@ -8,35 +8,41 @@ import plotly.layout.Layout object HoverOnTextBubbleChart extends DemoChart { def plotlyDocUrl = "https://plot.ly/javascript/bubble-charts/#hover-text-on-bubble-charts" - def id = "hover-on-text-bubble-chart" - def source = HoverOnTextBubbleChartSource.source + def id = "hover-on-text-bubble-chart" + def source = HoverOnTextBubbleChartSource.source // demo source start - - val trace1 = Scatter( - Seq(1, 2, 3, 4), - Seq(10, 11, 12, 13), - text = Seq("""A - size = 40""", """B - size = 60""", """C - size = 80""", """D - size = 100"""), - mode = ScatterMode(ScatterMode.Markers), - marker = Marker( - color = Seq(Color.RGB(93, 164, 214), Color.RGB(255, 144, 14), Color.RGB(44, 160, 101), Color.RGB(255, 65, 54)), - size = Seq(40, 60, 80, 100) + + val trace1 = Scatter(Seq(1, 2, 3, 4), Seq(10, 11, 12, 13)) + .withText( + Seq( + """A + size = 40""", + """B + size = 60""", + """C + size = 80""", + """D + size = 100""" + ) + ) + .withMode(ScatterMode(ScatterMode.Markers)) + .withMarker( + Marker() + .withColor( + Seq(Color.RGB(93, 164, 214), Color.RGB(255, 144, 14), Color.RGB(44, 160, 101), Color.RGB(255, 65, 54)) + ) + .withSize(Seq(40, 60, 80, 100)) ) - ) val data = Seq(trace1) - val layout = Layout( - title = "Bubble Chart Hover Text", - showlegend = false, - height = 400, - width = 600 - ) - + val layout = Layout() + .withTitle("Bubble Chart Hover Text") + .withShowlegend(false) + .withHeight(400) + .withWidth(600) + // demo source end } diff --git a/demo/src/main/scala/plotly/demo/heatmaps/AnnotatedHeatmap.scala b/demo/src/main/scala/plotly/demo/heatmaps/AnnotatedHeatmap.scala new file mode 100755 index 00000000..c8cbb035 --- /dev/null +++ b/demo/src/main/scala/plotly/demo/heatmaps/AnnotatedHeatmap.scala @@ -0,0 +1,58 @@ +package plotly.demo.heatmaps + +import plotly._ +import plotly.demo.DemoChart +import plotly.element._ +import plotly.layout._ + +object AnnotatedHeatmap extends DemoChart { + + def plotlyDocUrl = "https://plot.ly/javascript/heatmaps/#annotated-heatmap" + def id = "annotated-heatmap" + def source = AnnotatedHeatmapSource.source + + // demo source start + + val x = Seq("A", "B", "C", "D", "E"); + val y = Seq("W", "X", "Y", "Z"); + val z = Seq( + Seq(0.00, 0.00, 0.75, 0.75, 0.00), + Seq(0.00, 0.00, 0.75, 0.75, 0.00), + Seq(0.75, 0.75, 0.75, 0.75, 0.75), + Seq(0.00, 0.00, 0.00, 0.75, 0.00) + ) + + val data = Seq( + Heatmap(z, x, y) + .withShowscale(false) + .withColorscale( + ColorScale.CustomScale( + Seq( + (0, Color.StringColor("#3D9970")), + (1, Color.StringColor("#001f3f")) + ) + ) + ) + ) + + val layout = Layout() + .withTitle("Annotated Heatmap") + .withXaxis(Axis().withTicks(Ticks.Empty).withSide(Side.Top)) + .withYaxis(Axis().withTicks(Ticks.Empty).withTicksuffix(" ")) + .withAnnotations( + for { + (xv, xi) <- x.zipWithIndex + (yv, yi) <- y.zipWithIndex + } yield Annotation() + .withX(xv) + .withY(yv) + .withXref(Ref.Axis(AxisReference.X1)) + .withYref(Ref.Axis(AxisReference.Y1)) + .withShowarrow(false) + .withText(z(yi)(xi).toString) + .withFont(Font(Color.StringColor("white"))) + ) + + // demo source end + +} diff --git a/demo/src/main/scala/plotly/demo/heatmaps/BasicHeatmap.scala b/demo/src/main/scala/plotly/demo/heatmaps/BasicHeatmap.scala new file mode 100755 index 00000000..1d6d61bd --- /dev/null +++ b/demo/src/main/scala/plotly/demo/heatmaps/BasicHeatmap.scala @@ -0,0 +1,29 @@ +package plotly.demo.heatmaps + +import plotly._ +import plotly.demo.NoLayoutDemoChart +import plotly.element._ + +object BasicHeatmap extends NoLayoutDemoChart { + + def plotlyDocUrl = "https://plot.ly/javascript/heatmaps/#basic-heatmap" + def id = "basic-heatmap" + def source = BasicHeatmapSource.source + + // demo source start + + val data = Seq( + Heatmap() + .withZ( + Seq( + Seq(1, 20, 30), + Seq(20, 1, 60), + Seq(30, 60, 1) + ) + ) + .withColorscale(ColorScale.NamedScale("Portland")) + ) + + // demo source end + +} diff --git a/demo/src/main/scala/plotly/demo/heatmaps/CategoricalAxisHeatmap.scala b/demo/src/main/scala/plotly/demo/heatmaps/CategoricalAxisHeatmap.scala new file mode 100755 index 00000000..240497b2 --- /dev/null +++ b/demo/src/main/scala/plotly/demo/heatmaps/CategoricalAxisHeatmap.scala @@ -0,0 +1,30 @@ +package plotly.demo.heatmaps + +import plotly._ +import plotly.demo.NoLayoutDemoChart +import plotly.element._ + +object CategoricalAxisHeatmap extends NoLayoutDemoChart { + + def plotlyDocUrl = "https://plot.ly/javascript/heatmaps/#heatmap-with-categorical-axis-labels" + def id = "categorical-axis-heatmap" + def source = CategoricalAxisHeatmapSource.source + + // demo source start + + val data = Seq( + Heatmap() + .withZ( + Seq( + Seq(1, null.asInstanceOf[Int], 30, 50, 1), + Seq(20, 1, 60, 80, 30), + Seq(30, 60, 1, -10, 20) + ) + ) + .withX(Seq("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")) + .withY(Seq("Morning", "Afternoon", "Evening")) + ) + + // demo source end + +} diff --git a/demo/src/main/scala/plotly/demo/heatmaps/CustomColorScaleHeatmap.scala b/demo/src/main/scala/plotly/demo/heatmaps/CustomColorScaleHeatmap.scala new file mode 100755 index 00000000..5fa21f4e --- /dev/null +++ b/demo/src/main/scala/plotly/demo/heatmaps/CustomColorScaleHeatmap.scala @@ -0,0 +1,42 @@ +package plotly.demo.heatmaps + +import plotly._ +import plotly.demo.NoLayoutDemoChart +import plotly.element._ + +object CustomColorScaleHeatmap extends NoLayoutDemoChart { + + def plotlyDocUrl = "https://plot.ly/javascript/colorscales/#custom-colorscale-for-contour-plot" + def id = "custom-colorscale-heatmap" + def source = CustomColorScaleHeatmapSource.source + + // demo source start + + val data = Seq( + Heatmap() + .withZ( + Seq( + Seq(10.0, 10.625, 12.5, 15.625, 20.0), + Seq(5.625, 6.25, 8.125, 11.25, 15.625), + Seq(2.5, 3.125, 5.0, 8.125, 12.5), + Seq(0.625, 1.25, 3.125, 6.25, 10.625), + Seq(0.0, 0.625, 2.5, 5.625, 10.0) + ) + ) + .withColorscale( + ColorScale.CustomScale( + Seq( + (0, Color.RGB(166, 206, 227)), + (0.25, Color.RGB(31, 120, 180)), + (0.45, Color.RGB(178, 223, 138)), + (0.65, Color.RGB(51, 160, 44)), + (0.85, Color.RGB(251, 154, 153)), + (1, Color.RGB(227, 26, 28)) + ) + ) + ) + ) + + // demo source end + +} diff --git a/demo/src/main/scala/plotly/demo/histogram/BasicHistogram.scala b/demo/src/main/scala/plotly/demo/histogram/BasicHistogram.scala new file mode 100644 index 00000000..b6e9ba4d --- /dev/null +++ b/demo/src/main/scala/plotly/demo/histogram/BasicHistogram.scala @@ -0,0 +1,31 @@ +package plotly.demo.histogram + +import plotly.Histogram +import plotly.demo.DemoChart +import plotly.layout.Layout + +object BasicHistogram extends DemoChart { + + def plotlyDocUrl = "https://plotly.com/javascript/histograms/#basic-histogram" + def id = this.getClass.getSimpleName.dropRight(1) + def source = BasicHistogramSource.source + + // demo source start + + private val categoryCount = 50 + + private val indices = LazyList.from(0).take(categoryCount) + private val categories = indices.map(i => s"name-$i") + private val values = indices.map(_ => math.random()) + + val data = Seq(Histogram(values, categories)) + + val layout = new Layout() + .withTitle(id) + .withShowlegend(false) + .withHeight(400) + .withWidth(600) + + // demo source end + +} diff --git a/demo/src/main/scala/plotly/demo/histogram/StyledBasicHistogram.scala b/demo/src/main/scala/plotly/demo/histogram/StyledBasicHistogram.scala new file mode 100644 index 00000000..7dbd86fb --- /dev/null +++ b/demo/src/main/scala/plotly/demo/histogram/StyledBasicHistogram.scala @@ -0,0 +1,46 @@ +package plotly.demo.histogram + +import plotly.Histogram +import plotly.demo.DemoChart +import plotly.layout.{Axis, Layout, RangeSlider} +import plotly.element._ + +object StyledBasicHistogram extends DemoChart { + + def plotlyDocUrl = "https://plotly.com/javascript/histograms/#basic-histogram" + def id = this.getClass.getSimpleName.dropRight(1) + def source = StyledBasicHistogramSource.source + + // demo source start + + private val categoryCount = 50 + + private val indices = LazyList.from(0).take(categoryCount) + private val categories = indices.map(i => s"name-$i for $id") + private val values = indices.map(_ => math.random()) + + val data = Seq( + Histogram(values, categories) + .withMarker(new Marker().withColor(Color.StringColor("#004A72"))) + .withHovertext(categories.map(c => s"$c with hover text")) + ) + + val xAxis = new Axis() + .withRange((0d, 2d)) + .withRangeslider(RangeSlider()) + + val yAxis = new Axis() + .withTitle("Count") + .withFixedrange(true) + .withTickformat(".1f") + + val layout = new Layout() + .withXaxis(xAxis) + .withYaxis(yAxis) + .withTitle(id) + .withShowlegend(false) + .withHeight(400) + .withWidth(600) + + // demo source end +} diff --git a/demo/src/main/scala/plotly/demo/horizontalbarcharts/BasicHorizontalBarChart.scala b/demo/src/main/scala/plotly/demo/horizontalbarcharts/BasicHorizontalBarChart.scala index 0d66876d..a9d852ee 100644 --- a/demo/src/main/scala/plotly/demo/horizontalbarcharts/BasicHorizontalBarChart.scala +++ b/demo/src/main/scala/plotly/demo/horizontalbarcharts/BasicHorizontalBarChart.scala @@ -7,16 +7,15 @@ import plotly.element.Orientation object BasicHorizontalBarChart extends NoLayoutDemoChart { def plotlyDocUrl = "https://plot.ly/javascript/horizontal-bar-charts/#basic-horizontal-bar-chart" - def id = "basic-horizontal-bar-chart" - def source = BasicHorizontalBarChartSource.source + def id = "basic-horizontal-bar-chart" + def source = BasicHorizontalBarChartSource.source // demo source start - val data = Seq(Bar( - Seq(20, 14, 23), - Seq("giraffes", "orangutans", "monkeys"), - orientation = Orientation.Horizontal - )) + val data = Seq( + Bar(Seq(20, 14, 23), Seq("giraffes", "orangutans", "monkeys")) + .withOrientation(Orientation.Horizontal) + ) // demo source end diff --git a/demo/src/main/scala/plotly/demo/horizontalbarcharts/ColoredBarChart.scala b/demo/src/main/scala/plotly/demo/horizontalbarcharts/ColoredBarChart.scala index 914581c0..ad55631e 100644 --- a/demo/src/main/scala/plotly/demo/horizontalbarcharts/ColoredBarChart.scala +++ b/demo/src/main/scala/plotly/demo/horizontalbarcharts/ColoredBarChart.scala @@ -8,39 +8,34 @@ import plotly.layout.{BarMode, Layout} object ColoredBarChart extends DemoChart { def plotlyDocUrl = "https://plot.ly/javascript/horizontal-bar-charts/#colored-bar-chart" - def id = "colored-bar-chart" - def source = ColoredBarChartSource.source + def id = "colored-bar-chart" + def source = ColoredBarChartSource.source // demo source start - val trace1 = Bar( - Seq(20, 14, 23), - Seq("giraffes", "orangutans", "monkeys"), - name = "SF Zoo", - orientation = Orientation.Horizontal, - marker = Marker( - color = Color.RGBA(55, 128, 191, 0.6), - width = 1 + val trace1 = Bar(Seq(20, 14, 23), Seq("giraffes", "orangutans", "monkeys")) + .withName("SF Zoo") + .withOrientation(Orientation.Horizontal) + .withMarker( + Marker() + .withColor(Color.RGBA(55, 128, 191, 0.6)) + .withWidth(1) ) - ) - - val trace2 = Bar( - Seq(12, 18, 29), - Seq("giraffes", "orangutans", "monkeys"), - name = "LA Zoo", - orientation = Orientation.Horizontal, - marker = Marker( - color = Color.RGBA(255, 153, 51, 0.6), - width = 1 + + val trace2 = Bar(Seq(12, 18, 29), Seq("giraffes", "orangutans", "monkeys")) + .withName("LA Zoo") + .withOrientation(Orientation.Horizontal) + .withMarker( + Marker() + .withColor(Color.RGBA(255, 153, 51, 0.6)) + .withWidth(1) ) - ) val data = Seq(trace1, trace2) - val layout = Layout( - title = "Colored Bar Chart", - barmode = BarMode.Stack - ) + val layout = Layout() + .withTitle("Colored Bar Chart") + .withBarmode(BarMode.Stack) // demo source end diff --git a/demo/src/main/scala/plotly/demo/lineandscatter/CategoricalDotPlot.scala b/demo/src/main/scala/plotly/demo/lineandscatter/CategoricalDotPlot.scala index 08f4489a..72925dfe 100644 --- a/demo/src/main/scala/plotly/demo/lineandscatter/CategoricalDotPlot.scala +++ b/demo/src/main/scala/plotly/demo/lineandscatter/CategoricalDotPlot.scala @@ -36,76 +36,75 @@ object CategoricalDotPlot extends DemoChart { 49.1, 42.0, 52.7, 84.3, 51.7, 61.1, 55.3, 64.2, 91.1, 58.9 ) - val trace1 = Scatter( - votingPop, - country, - mode = ScatterMode(ScatterMode.Markers), - name = "Percent of estimated voting age population", - marker = Marker( - color = Color.RGBA(156, 165, 196, 0.95), - line = Line( - color = Color.RGBA(156, 165, 196, 1.0), - width = 1.0 - ), - symbol = Symbol.Circle(), - size = 16 + val trace1 = Scatter(votingPop, country) + .withMode(ScatterMode(ScatterMode.Markers)) + .withName("Percent of estimated voting age population") + .withMarker( + Marker() + .withColor(Color.RGBA(156, 165, 196, 0.95)) + .withLine( + Line() + .withColor(Color.RGBA(156, 165, 196, 1.0)) + .withWidth(1.0) + ) + .withSymbol(Symbol.Circle()) + .withSize(16) ) - ) - val trace2 = Scatter( - regVoters, - country, - mode = ScatterMode(ScatterMode.Markers), - name = "Percent of estimated registered voters", - marker = Marker( - color = Color.RGBA(204, 204, 204, 0.95), - line = Line( - color = Color.RGBA(217, 217, 217, 1.0), - width = 1.0 - ), - symbol = Symbol.Circle(), - size = 16 + val trace2 = Scatter(regVoters, country) + .withMode(ScatterMode(ScatterMode.Markers)) + .withName("Percent of estimated registered voters") + .withMarker( + Marker() + .withColor(Color.RGBA(204, 204, 204, 0.95)) + .withLine( + Line() + .withColor(Color.RGBA(217, 217, 217, 1.0)) + .withWidth(1.0) + ) + .withSymbol(Symbol.Circle()) + .withSize(16) ) - ) val data = Seq(trace1, trace2) - val layout = Layout( - title = "Votes cast for ten lowest voting age population in OECD countries", - xaxis = Axis( - showgrid = false, - showline = true, - linecolor = Color.RGB(102, 102, 102), - titlefont = Font( - color = Color.RGB(204, 204, 204) - ), - tickfont = Font( - color = Color.RGB(102, 102, 102) - ), - autotick = false, - dtick = 10.0, - ticks = Ticks.Outside, - tickcolor = Color.RGB(102, 102, 102) - ), - margin = Margin( - l = 140, - r = 40, - b = 50, - t = 80 - ), - legend = Legend( - font = Font( - size = 10 - ), - yanchor = Anchor.Middle, - xanchor = Anchor.Right - ), - width = 600, - height = 400, - paper_bgcolor = Color.RGB(254, 247, 234), - plot_bgcolor = Color.RGB(254, 247, 234), - hovermode = HoverMode.Closest - ) + val layout = Layout() + .withTitle("Votes cast for ten lowest voting age population in OECD countries") + .withXaxis( + Axis() + .withShowgrid(false) + .withShowline(true) + .withLinecolor(Color.RGB(102, 102, 102)) + .withTitlefont( + Font(Color.RGB(204, 204, 204)) + ) + .withTickfont( + Font(Color.RGB(102, 102, 102)) + ) + .withAutotick(false) + .withDtick(10.0) + .withTicks(Ticks.Outside) + .withTickcolor(Color.RGB(102, 102, 102)) + ) + .withMargin( + Margin( + l = 140, + r = 40, + b = 50, + t = 80 + ) + ) + .withLegend( + Legend() + .withFont(Font(size = 10)) + .withYanchor(Anchor.Middle) + .withXanchor(Anchor.Right) + ) + .withWidth(600) + .withHeight(400) + .withPaper_bgcolor(Color.RGB(254, 247, 234)) + .withPlot_bgcolor(Color.RGB(254, 247, 234)) + .withHovermode(HoverMode.Closest) // demo source end diff --git a/demo/src/main/scala/plotly/demo/linecharts/LineAndScatterPlot.scala b/demo/src/main/scala/plotly/demo/linecharts/LineAndScatterPlot.scala index c4a73856..e20eff5a 100644 --- a/demo/src/main/scala/plotly/demo/linecharts/LineAndScatterPlot.scala +++ b/demo/src/main/scala/plotly/demo/linecharts/LineAndScatterPlot.scala @@ -15,29 +15,19 @@ object LineAndScatterPlot extends DemoChart { // demo source start - val trace1 = Scatter( - Seq(1, 2, 3, 4), - Seq(10, 15, 13, 17), - mode = ScatterMode(ScatterMode.Markers) - ) - - val trace2 = Scatter( - Seq(2, 3, 4, 5), - Seq(16, 5, 11, 9), - mode = ScatterMode(ScatterMode.Lines) - ) - - val trace3 = Scatter( - Seq(1, 2, 3, 4), - Seq(12, 9, 15, 12), - mode = ScatterMode(ScatterMode.Lines, ScatterMode.Markers) - ) + val trace1 = Scatter(Seq(1, 2, 3, 4), Seq(10, 15, 13, 17)) + .withMode(ScatterMode(ScatterMode.Markers)) + + val trace2 = Scatter(Seq(2, 3, 4, 5), Seq(16, 5, 11, 9)) + .withMode(ScatterMode(ScatterMode.Lines)) + + val trace3 = Scatter(Seq(1, 2, 3, 4), Seq(12, 9, 15, 12)) + .withMode(ScatterMode(ScatterMode.Lines, ScatterMode.Markers)) val data = Seq(trace1, trace2, trace3) - val layout = Layout( - title = "Line and Scatter Plot" - ) + val layout = Layout() + .withTitle("Line and Scatter Plot") // demo source end diff --git a/demo/src/main/scala/plotly/demo/timeseries/TimeSeries.scala b/demo/src/main/scala/plotly/demo/timeseries/TimeSeries.scala index 803a730e..6fdced95 100644 --- a/demo/src/main/scala/plotly/demo/timeseries/TimeSeries.scala +++ b/demo/src/main/scala/plotly/demo/timeseries/TimeSeries.scala @@ -6,8 +6,8 @@ import plotly.demo.NoLayoutDemoChart object TimeSeries extends NoLayoutDemoChart { def plotlyDocUrl = "https://plot.ly/javascript/time-series/#date-strings" - def id = "time-series-chart" - def source = TimeSeriesSource.source + def id = "time-series-chart" + def source = TimeSeriesSource.source // demo source start diff --git a/joda-time/src/main/scala/plotly/Joda.scala b/joda-time/src/main/scala/plotly/Joda.scala index 56ed11c8..f22aade3 100644 --- a/joda-time/src/main/scala/plotly/Joda.scala +++ b/joda-time/src/main/scala/plotly/Joda.scala @@ -3,6 +3,8 @@ package plotly import org.joda.time._ import plotly.Sequence.DateTimes +import scala.language.implicitConversions + object Joda { implicit def fromJodaLocalDates(seq: Seq[LocalDate]): Sequence = diff --git a/jupyter-scala/src/main/scala/plotly/JupyterScala.scala b/jupyter-scala/src/main/scala/plotly/JupyterScala.scala deleted file mode 100644 index b2a0155c..00000000 --- a/jupyter-scala/src/main/scala/plotly/JupyterScala.scala +++ /dev/null @@ -1,242 +0,0 @@ -package plotly - -import jupyter.api.Publish - -import java.lang.{ Integer => JInt, Double => JDouble, Boolean => JBoolean } - -import scala.util.Random - -import plotly.element._ -import plotly.layout._ - -object JupyterScala { - - def init(offline: Boolean = false)(implicit publish: Publish): Unit = { - - // offline mode like in plotly-python - - val requireInit = - if (offline) - s"""define('plotly', function(require, exports, module) { - | ${Plotly.plotlyMinJs} - |}); - """ - else - """require.config({ - | paths: { - | d3: 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min', - | plotly: 'https://cdn.plot.ly/plotly-1.12.0.min' - | }, - | - | shim: { - | plotly: { - | deps: ['d3', 'jquery'], - | exports: 'plotly' - | } - | } - |}); - """.stripMargin - - val html = s""" - - """ - - publish.html(html) - } - - def plotJs( - div: String, - data: Seq[Trace], - layout: Layout - )(implicit - publish: Publish - ): Unit = { - - val baseJs = Plotly.jsSnippet(div, data, layout) - - val js = - s"""requirejs(["plotly"], function(Plotly) { - | $baseJs - |}); - """.stripMargin - publish.js(js) - } - - def randomDiv() = "plot-" + math.abs(Random.nextInt().toLong) - - def plot( - data: Seq[Trace], - layout: Layout = Layout(), - div: String = "" - )(implicit - publish: Publish - ): String = { - - val div0 = - if (div.isEmpty) - randomDiv() - else - div - - if (div.isEmpty) - publish.html(s"""
""")
-
- plotJs(div0, data, layout)
-
- div0
- }
-
- implicit class DataOps(val data: Trace) extends AnyVal {
-
- def plot(
- title: String = null,
- legend: Legend = null,
- width: JInt = null,
- height: JInt = null,
- showlegend: JBoolean = null,
- xaxis: Axis = null,
- yaxis: Axis = null,
- xaxis1: Axis = null,
- xaxis2: Axis = null,
- xaxis3: Axis = null,
- xaxis4: Axis = null,
- yaxis1: Axis = null,
- yaxis2: Axis = null,
- yaxis3: Axis = null,
- yaxis4: Axis = null,
- barmode: BarMode = null,
- autosize: JBoolean = null,
- margin: Margin = null,
- annotations: Seq[Annotation] = null,
- plot_bgcolor: Color = null,
- paper_bgcolor: Color = null,
- font: Font = null,
- bargap: JDouble = null,
- bargroupgap: JDouble = null,
- hovermode: HoverMode = null,
- boxmode: BoxMode = null,
- div: String = ""
- )(implicit
- publish: Publish
- ): String =
- plot(
- Layout(
- title,
- legend,
- width,
- height,
- showlegend,
- xaxis,
- yaxis,
- xaxis1,
- xaxis2,
- xaxis3,
- xaxis4,
- yaxis1,
- yaxis2,
- yaxis3,
- yaxis4,
- barmode,
- autosize,
- margin,
- annotations,
- plot_bgcolor,
- paper_bgcolor,
- font,
- bargap,
- bargroupgap,
- hovermode,
- boxmode
- ),
- div
- )
-
- def plot(
- layout: Layout,
- div: String
- )(implicit
- publish: Publish
- ): String =
- JupyterScala.plot(Seq(data), layout, div = div)
- }
-
- implicit class DataSeqOps(val data: Seq[Trace]) extends AnyVal {
- def plot(
- title: String = null,
- legend: Legend = null,
- width: JInt = null,
- height: JInt = null,
- showlegend: JBoolean = null,
- xaxis: Axis = null,
- yaxis: Axis = null,
- xaxis1: Axis = null,
- xaxis2: Axis = null,
- xaxis3: Axis = null,
- xaxis4: Axis = null,
- yaxis1: Axis = null,
- yaxis2: Axis = null,
- yaxis3: Axis = null,
- yaxis4: Axis = null,
- barmode: BarMode = null,
- autosize: JBoolean = null,
- margin: Margin = null,
- annotations: Seq[Annotation] = null,
- plot_bgcolor: Color = null,
- paper_bgcolor: Color = null,
- font: Font = null,
- bargap: JDouble = null,
- bargroupgap: JDouble = null,
- hovermode: HoverMode = null,
- boxmode: BoxMode = null,
- div: String = ""
- )(implicit
- publish: Publish
- ): String =
- plot(
- Layout(
- title,
- legend,
- width,
- height,
- showlegend,
- xaxis,
- yaxis,
- xaxis1,
- xaxis2,
- xaxis3,
- xaxis4,
- yaxis1,
- yaxis2,
- yaxis3,
- yaxis4,
- barmode,
- autosize,
- margin,
- annotations,
- plot_bgcolor,
- paper_bgcolor,
- font,
- bargap,
- bargroupgap,
- hovermode,
- boxmode
- ),
- div
- )
-
- def plot(
- layout: Layout,
- div: String
- )(implicit
- publish: Publish
- ): String =
- JupyterScala.plot(data, layout, div = div)
- }
-
-}
diff --git a/plotly-documentation b/plotly-documentation
index 49469d3e..20b627cf 160000
--- a/plotly-documentation
+++ b/plotly-documentation
@@ -1 +1 @@
-Subproject commit 49469d3e4f1295dc383384535d11573928134a6c
+Subproject commit 20b627cf49db521172221c28788509621bc9449e
diff --git a/project/Aliases.scala b/project/Aliases.scala
deleted file mode 100644
index 12acbf45..00000000
--- a/project/Aliases.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-
-import sbt._
-import sbt.Keys._
-
-object Aliases {
-
- def libs = libraryDependencies
-
-}
diff --git a/project/Deps.scala b/project/Deps.scala
index 2fbedcbc..704a7ae2 100644
--- a/project/Deps.scala
+++ b/project/Deps.scala
@@ -1,28 +1,18 @@
-
import sbt._
-import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
+import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
object Deps {
import Def.setting
- private val jupyterScalaVersion = "0.4.0"
- private val circeVersion = "0.6.1"
-
-
- def circeCore = setting("io.circe" %%% "circe-core" % circeVersion)
- def circeLiteral = setting("io.circe" %% "circe-literal" % circeVersion)
- def circeScalaJs = setting("io.circe" %%% "circe-scalajs" % circeVersion)
- def circeParser = setting("io.circe" %%% "circe-parser" % circeVersion)
- def jodaTime = "joda-time" % "joda-time" % "2.9.1"
- def jupyterScalaApi = "org.jupyter-scala" % "scala-api" % jupyterScalaVersion cross CrossVersion.full
- def rhino = "org.mozilla" % "rhino" % "1.7.7.1"
- def shapeless = setting("com.chuusai" %%% "shapeless" % "2.3.2")
- def scalacheckShapeless = setting("com.github.alexarchambault" %%% "scalacheck-shapeless_1.13" % "1.1.7")
- def scalajsDom = setting("org.scala-js" %%% "scalajs-dom" % "0.9.3")
- def scalatags = setting("com.lihaoyi" %%% "scalatags" % "0.6.7")
- def scalaTest = "org.scalatest" %% "scalatest" % "3.0.4"
- def utest = setting("com.lihaoyi" %%% "utest" % "0.5.4")
+ def almondScalaApi = "sh.almond" %% "jupyter-api" % "0.13.14"
+ def argonautShapeless = setting("com.github.alexarchambault" %%% "argonaut-shapeless_6.3" % "1.3.1")
+ def dataClass = "io.github.alexarchambault" %% "data-class" % "0.2.6"
+ def jodaTime = "joda-time" % "joda-time" % "2.12.7"
+ def rhino = "org.mozilla" % "rhino" % "1.7.15"
+ def scalajsDom = setting("org.scala-js" %%% "scalajs-dom" % "2.8.0")
+ def scalatags = setting("com.lihaoyi" %%% "scalatags" % "0.13.1")
+ def scalaTest = "org.scalatest" %% "scalatest" % "3.2.18"
}
diff --git a/project/Mima.scala b/project/Mima.scala
new file mode 100644
index 00000000..ba0d0a1d
--- /dev/null
+++ b/project/Mima.scala
@@ -0,0 +1,17 @@
+import com.typesafe.tools.mima.core._
+import com.typesafe.tools.mima.plugin.MimaPlugin
+import sbt._
+import sbt.Keys._
+
+import scala.sys.process._
+
+object Mima {
+
+ lazy val renderFilters = Def.settings(
+ MimaPlugin.autoImport.mimaBinaryIssueFilters ++= Seq(
+ // users shouln't ever reference those
+ ProblemFilters.exclude[Problem]("plotly.internals.shaded.*")
+ )
+ )
+
+}
diff --git a/project/Settings.scala b/project/Settings.scala
index d0d91b8e..765b1b1f 100644
--- a/project/Settings.scala
+++ b/project/Settings.scala
@@ -1,17 +1,15 @@
-
+import com.jsuereth.sbtpgp._
import sbt._
import sbt.Keys._
-import Aliases._
-
object Settings {
lazy val customSourceGenerators = TaskKey[Seq[sbt.File]]("custom-source-generators")
lazy val generateCustomSources = Seq(
customSourceGenerators := {
- var dir = target.value
- val f = dir / "Properties.scala"
+ val dir = target.value
+ val f = dir / "Properties.scala"
dir.mkdirs()
def gitCommit =
@@ -27,8 +25,7 @@ object Settings {
| val commitHash = "$gitCommit"
|
|}
- """
- .stripMargin
+ """.stripMargin
.getBytes("UTF-8")
)
w.close()
@@ -42,14 +39,12 @@ object Settings {
def process(destDir: File, pathComponents: Seq[String], file: File): Unit = {
if (file.isDirectory) {
- val destDir0 = destDir / file.getName
+ val destDir0 = destDir / file.getName
val pathComponents0 = pathComponents :+ file.getName
for (f <- file.listFiles())
process(destDir0, pathComponents0, f)
} else {
- val lines = new String(java.nio.file.Files.readAllBytes(file.toPath), "UTF-8")
- .linesIterator
- .toVector
+ val lines = new String(java.nio.file.Files.readAllBytes(file.toPath), "UTF-8").linesIterator.toVector
val demoLines = lines
.dropWhile(!_.contains("demo source start"))
@@ -69,8 +64,7 @@ object Settings {
| val source = $tq${demoLines.mkString("\n")}$tq
|
|}
- """
- .stripMargin
+ """.stripMargin
.getBytes("UTF-8")
)
w.close()
@@ -80,72 +74,67 @@ object Settings {
}
}
- process(dir / "plotly", Vector(), scalaSource.in(Compile).value / "plotly" / "demo")
+ process(dir / "plotly", Vector(), (Compile / scalaSource).value / "plotly" / "demo")
files
},
- sourceGenerators.in(Compile) += customSourceGenerators.taskValue
+ (Compile / sourceGenerators) += customSourceGenerators.taskValue
)
- lazy val shared = Seq(
- organization := "org.plotly-scala",
- scalacOptions ++= {
- if (scalaBinaryVersion.value == "2.12")
- Seq()
- else
- Seq("-target:jvm-1.7")
- },
- resolvers ++= Seq(
- "Webjars Bintray" at "https://dl.bintray.com/webjars/maven/",
- Resolver.sonatypeRepo("releases")
- ),
- publishMavenStyle := true,
- licenses := Seq("LGPL 3.0" -> url("http://opensource.org/licenses/LGPL-3.0")),
- homepage := Some(url("https://github.com/alexarchambault/plotly-scala")),
- pomExtra := {
-
+ | |
|$plotlyHeader | |
|