├── .gitignore ├── LICENSE ├── README.md ├── ace └── src │ └── main │ └── scala │ └── scaladget │ └── ace │ ├── Utils.scala │ └── ace.scala ├── acediff └── src │ └── main │ └── scala │ └── scaladget │ └── acediff │ └── acediff.scala ├── bootstrapDemo ├── package-lock.json └── src │ └── main │ ├── resources │ ├── bootstrap-native.html │ ├── css │ │ ├── bootstrap.css │ │ ├── github-gist.css │ │ ├── nouislider.min.css │ │ └── style.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── img │ │ ├── bootstrapslider.png │ │ ├── flowchart.png │ │ ├── iscpif.png │ │ ├── modal.png │ │ ├── svgstar.png │ │ ├── trigger.png │ │ └── triggered.png │ └── scala │ └── demo │ ├── AceDemo.scala │ ├── ButtonDemo.scala │ ├── CollapseDemo.scala │ ├── Demo.scala │ ├── FormDemo.scala │ ├── LabelDemo.scala │ ├── ModalDialogDemo.scala │ ├── NavBarDemo.scala │ ├── PlayGround.scala │ ├── PopoverDemo.scala │ ├── SelectDemo.scala │ ├── SliderDemo.scala │ ├── StartDemo.scala │ ├── TabDemo.scala │ ├── TableDemo.scala │ ├── ToastDemo.scala │ ├── TooltipDemo.scala │ └── Utils.scala ├── bootstrapnative └── src │ └── main │ └── scala │ └── scaladget │ └── bootstrapnative │ ├── BootstrapTags.scala │ ├── JsDependency.scala │ ├── Popup.scala │ ├── Selector.scala │ ├── Table.scala │ ├── Tools.scala │ ├── bootstrapnative.scala │ └── stylesheet.scala ├── build.sbt ├── flowchartDemo └── src │ └── main │ ├── resources │ ├── css │ │ ├── github-gist.css │ │ ├── style.css │ │ └── styleGraph.css │ ├── flowchart.html │ └── img │ │ └── svgstar.png │ └── scala │ └── demo │ ├── FlowChart.scala │ └── FlowChartDemo.scala ├── highlightjs └── src │ └── main │ └── scala │ └── scaladget │ └── highlightjs │ └── HighlightJS.scala ├── lunr └── src │ └── main │ └── scala │ └── scaladget │ └── lunr │ └── lunr.scala ├── nouislider └── src │ └── main │ └── scala │ └── nouislider.scala ├── project ├── build.properties └── plugins.sbt ├── svg └── src │ └── main │ └── scala │ └── scaladget │ └── svg │ └── svg.scala ├── svgDemo └── src │ └── main │ ├── resources │ ├── css │ │ ├── github-gist.css │ │ └── style.css │ ├── grid.html │ ├── img │ │ └── svgstar.png │ └── svg.html │ └── scala │ └── demo │ ├── Demo.scala │ ├── SVGDemo.scala │ └── SVGStarDemo.scala ├── tools └── src │ └── main │ └── scala │ └── scaladget │ └── tools │ └── ElementListener.scala └── version.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | target/ 3 | target/* 4 | *.orig 5 | build 6 | build/* 7 | dist 8 | dist/* 9 | private 10 | private/* 11 | release/ 12 | release/* 13 | *.iml 14 | *.ipr 15 | *.iws 16 | *.iml 17 | .idea/ 18 | .idea/* 19 | *~ 20 | .project 21 | .settings 22 | *.bak 23 | examples-opt.js 24 | examples-preopt.js 25 | .bsp/ 26 | .bsp/* 27 | *package-lock.json 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | scaladget 3 | ========= 4 | 5 | Scaladget provides a scala facades of some famous javascript libraries. It relies on the [scala-js](http://www.scala-js.org/) project. Among them: 6 | * [Bootstrap-native.js](https://thednp.github.io/bootstrap.native/) 7 | * [Sortable.js](http://rubaxa.github.io/Sortable/) 8 | * [Ace Editor](http://ace.c9.io) 9 | * [Bootstrap-slider](http://seiyria.com/bootstrap-slider/) 10 | * [Lunr](https://lunrjs.com/) 11 | 12 | 13 | ## Usage ## 14 | Just add this to your dependencies: 15 | ```sh 16 | libraryDependencies += "org.openmole.scaladget" %%% "bootstrapnative" % scaladgetVersion 17 | libraryDependencies += "org.openmole.scaladget" %%% "ace" % scaladgetVersion 18 | libraryDependencies += "org.openmole.scaladget" %%% "bootstrapslider" % scaladgetVersion 19 | libraryDependencies += "org.openmole.scaladget" %%% "lunr" % scaladgetVersion 20 | ``` 21 | 22 | Current version : see release tags section 23 | 24 | All the facades are intensively used in the [OpenMOLE project](https://github.com/openmole/openmole). 25 | 26 | ## Bootstrap-native library ## 27 | [Demo](http://zebulon.iscpif.fr/~leclaire/scaladget/) 28 | 29 | The boostrap-native facade (based on [https://thednp.github.io/bootstrap.native/](http://zebulon.iscpif.fr/~leclaire/scaladget/)) renders transparent the use of buttons, forms, modals, tables, collapsers, selectors etc... 30 | 31 | Imports: 32 | ```scala 33 | import scaladget.bootstrapnative.bsn._ 34 | ``` 35 | 36 | Here is an example of bootstrap modal dialog creation in full scala: 37 | ```scala 38 | import scalatags.JsDom.tags 39 | 40 | // Create the Modal dialog 41 | val modalDialog: ModalDialog = 42 | ModalDialog( 43 | onopen = ()=> println("OPEN"), 44 | onclose = ()=> println("CLOSE") 45 | ) 46 | 47 | // Append header, body, footer elements 48 | modalDialog header div("Header") 49 | modalDialog footer buttonGroup()( 50 | ModalDialog.closeButton(modalDialog, btn_info, "OK"), 51 | ModalDialog.closeButton(modalDialog, btn_default, "Cancel") 52 | ) 53 | 54 | // Build the dialog and the modal dialog 55 | tags.span( 56 | modalDialog.dialog, 57 | button("Modal !", onclick := {() => modalDialog.show}, btn_primary, marginLeft := 5), 58 | tags.span(glyph_settings, paddingLeft := 5, pointer, onclick := {()=> modalDialog.show}) 59 | ``` 60 | ![modal](demo/src/main/resources/img/modal.png) 61 | 62 | And here an example for a dynamic collapser: 63 | 64 | ```scala 65 | buttonIcon("Trigger !", btn_primary).expandOnclick(panel("My text in detail")(width := 400)) 66 | ``` 67 | ![trigger](demo/src/main/resources/img/trigger.png) 68 | 69 | ![triggered](demo/src/main/resources/img/triggered.png) 70 | 71 | Find more examples on the: [API Demo](http://zebulon.iscpif.fr/~leclaire/scaladget/) 72 | Here is an example of scaladget intensive use in the [OpenMOLE project](https://github.com/openmole/openmole/blob/master/openmole/gui/client/org.openmole.gui.client.core/src/main/scala/org/openmole/gui/client/core/ScriptClient.scala) 73 | 74 | ## Bootstrap-slider library ## 75 | See rendering in the [API Demo](http://zebulon.iscpif.fr/~leclaire/scaladget/) 76 | 77 | ![bootstrapslider](demo/src/main/resources/img/bootstrapslider.png) 78 | 79 | ## SVG rendering ## 80 | A SVG API for rendering typed path in SVG is provided. it is compatible with [scalatags](https://github.com/lihaoyi/scalatags). 81 | See a simple example [here](http://zebulon.iscpif.fr/~leclaire/scaladget/svg.html) 82 | 83 | ![svg](demo/src/main/resources/img/svgstar.png) 84 | 85 | An example using the scaladget SVG tool and reactive library [Laminar](https://laminar.dev/) can be found in this [demo](http://zebulon.iscpif.fr/~leclaire/scaladget/flowchart.html). 86 | It reproduces this [D3 flowchart](http://bl.ocks.org/cjrd/6863459). 87 | 88 | 89 | ![flowchart](demo/src/main/resources/img/flowchart.png) 90 | 91 | -------------------------------------------------------------------------------- /ace/src/main/scala/scaladget/ace/Utils.scala: -------------------------------------------------------------------------------- 1 | package scaladget.ace 2 | 3 | import com.raquo.laminar.api.L._ 4 | 5 | import scala.scalajs.js 6 | import org.scalajs.dom 7 | 8 | object Utils { 9 | 10 | def rangeFor(startRow: Int, startCol: Int, endRow: Int, endCol: Int) = 11 | js.Dynamic.newInstance(ace.require("ace/range").Range)(startRow, startCol, endRow, endCol).asInstanceOf[scaladget.ace.Range] 12 | 13 | def getBreakPointElements(editorDiv: HtmlElement) = { 14 | editorDiv.ref.getElementsByClassName("ace_breakpoint").map {e=> 15 | e-> e.innerText 16 | }} 17 | } 18 | -------------------------------------------------------------------------------- /acediff/src/main/scala/scaladget/acediff/acediff.scala: -------------------------------------------------------------------------------- 1 | package scaladget.acediff 2 | 3 | import scala.scalajs.js 4 | import org.scalajs.dom.raw.HTMLElement 5 | import org.querki.jsext._ 6 | import scaladget.ace.Editor 7 | 8 | import scala.scalajs.js 9 | import js.annotation._ 10 | import js.| 11 | 12 | object aceDiff { 13 | def apply() = AceDiffConstructorBuilder 14 | } 15 | 16 | @js.native 17 | @JSGlobal 18 | class AceDiff protected() extends js.Object { 19 | def this(opts: AceDiffConstructor) = this() 20 | 21 | def getEditors(): Editors = js.native 22 | 23 | def getNumDiffs(): Double = js.native 24 | 25 | def diff(): Unit = js.native 26 | 27 | def destroy(): Unit = js.native 28 | } 29 | 30 | @js.native 31 | trait AceDiffConstructor extends js.Object { 32 | var element: js.UndefOr[String | HTMLElement] = js.native 33 | 34 | var left: js.UndefOr[LR] = js.native 35 | 36 | var right: js.UndefOr[LR] = js.native 37 | 38 | var mode: String = js.native 39 | 40 | var theme: String = js.native 41 | 42 | var diffGranularity: String = js.native 43 | 44 | var showDiffs: Boolean = js.native 45 | 46 | var showConnectors: Boolean = js.native 47 | 48 | var maxDiffs: Double = js.native 49 | } 50 | 51 | object AceDiffConstructorBuilder extends AceDiffConstructorBuilder(noOpts) 52 | 53 | class AceDiffConstructorBuilder(val dict: OptMap) extends JSOptionBuilder[AceDiffConstructor, AceDiffConstructorBuilder](new AceDiffConstructorBuilder(_)) { 54 | 55 | def element(v: String | HTMLElement) = jsOpt("element", v) 56 | 57 | def left(v: LR) = jsOpt("left", v) 58 | 59 | def right(v: LR) = jsOpt("right", v) 60 | 61 | def showDiffs(v: Boolean) = jsOpt("showDiffs", v) 62 | 63 | def mode(v: String) = jsOpt("mode", v) 64 | 65 | def theme(v: String) = jsOpt("theme", v) 66 | 67 | def diffGranularity(v: String) = jsOpt("diffGranularity", v) 68 | 69 | def showConnectors(v: Boolean) = jsOpt("showConnectors", v) 70 | 71 | def maxDiffs(v: Double) = jsOpt("maxDiffs", v) 72 | 73 | def build = new AceDiff(this) 74 | } 75 | 76 | @js.native 77 | trait LR extends js.Object { 78 | def content: String = js.native 79 | 80 | var mode: js.UndefOr[String] = js.native 81 | 82 | var theme: js.UndefOr[String] = js.native 83 | 84 | var editable: js.UndefOr[Boolean] = js.native 85 | 86 | var copyLinkEnabled: js.UndefOr[Boolean] = js.native 87 | 88 | } 89 | 90 | object LR extends LRBuilder(noOpts) 91 | 92 | class LRBuilder(val dict: OptMap) extends JSOptionBuilder[LR, LRBuilder](new LRBuilder(_)) { 93 | 94 | def content(v: String) = jsOpt("content", v) 95 | 96 | def mode(v: String) = jsOpt("mode", v) 97 | 98 | def theme(v: String) = jsOpt("theme", v) 99 | 100 | def editable(v: Boolean) = jsOpt("editable", v) 101 | 102 | def copyLinkEnabled(v: Boolean) = jsOpt("copyLinkEnabled", v) 103 | } 104 | 105 | @js.native 106 | trait Editors extends js.Object { 107 | var right: Editor = js.native 108 | 109 | var left: Editor = js.native 110 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/bootstrap-native.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/css/github-gist.css: -------------------------------------------------------------------------------- 1 | /** 2 | * GitHub Gist Theme 3 | * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | background: white; 9 | padding: 0.5em; 10 | color: #333333; 11 | overflow-x: auto; 12 | } 13 | 14 | .hljs-comment, 15 | .hljs-meta { 16 | color: #969896; 17 | } 18 | 19 | .hljs-string, 20 | .hljs-variable, 21 | .hljs-template-variable, 22 | .hljs-strong, 23 | .hljs-emphasis, 24 | .hljs-quote { 25 | color: #df5000; 26 | } 27 | 28 | .hljs-keyword, 29 | .hljs-selector-tag, 30 | .hljs-type { 31 | color: #a71d5d; 32 | } 33 | 34 | .hljs-literal, 35 | .hljs-symbol, 36 | .hljs-bullet, 37 | .hljs-attribute { 38 | color: #0086b3; 39 | } 40 | 41 | .hljs-section, 42 | .hljs-name { 43 | color: #63a35c; 44 | } 45 | 46 | .hljs-tag { 47 | color: #333333; 48 | } 49 | 50 | .hljs-title, 51 | .hljs-attr, 52 | .hljs-selector-id, 53 | .hljs-selector-class, 54 | .hljs-selector-attr, 55 | .hljs-selector-pseudo { 56 | color: #795da3; 57 | } 58 | 59 | .hljs-addition { 60 | color: #55a532; 61 | background-color: #eaffea; 62 | } 63 | 64 | .hljs-deletion { 65 | color: #bd2c00; 66 | background-color: #ffecec; 67 | } 68 | 69 | .hljs-link { 70 | text-decoration: underline; 71 | } 72 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/css/nouislider.min.css: -------------------------------------------------------------------------------- 1 | .noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;height:100%;width:100%;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{top:-100%;width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;bottom:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px} -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/css/style.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family:'Glyphicons Halflings'; 3 | src:url(../fonts/glyphicons-halflings-regular.eot); 4 | } 5 | 6 | code.scala.hljs{ 7 | background-color: #f5f5f5; 8 | } 9 | 10 | /* enables CSS3transition for simple collapses, accordion and also navbar in responsive view */ 11 | .collapse { transition: height .3s ease-out; /*-webkit-transition: height .3s ease-out*/ } 12 | 13 | /* styles the close button for the dismissible popovers */ 14 | .popover .close { position: absolute; top: 7px; right: 10px; } 15 | 16 | h3 { 17 | color: inherit; 18 | font-family: inherit; 19 | font-weight: 700; 20 | line-height: 1.9; 21 | } 22 | 23 | //Some bootstrap 4 CCS 24 | 25 | .thead-inverse th { 26 | color: #fff; 27 | background-color: #292b2c; 28 | } 29 | 30 | .thead-default th { 31 | color: #464a4c; 32 | background-color: #eceeef; 33 | } 34 | 35 | .table-inverse { 36 | color: #fff; 37 | background-color: #292b2c; 38 | } 39 | 40 | .table-inverse th, 41 | .table-inverse td, 42 | .table-inverse thead th { 43 | border-color: #fff; 44 | } 45 | 46 | .table-inverse.table-bordered { 47 | border: 0; 48 | } 49 | 50 | .table-responsive { 51 | display: block; 52 | width: 100%; 53 | overflow-x: auto; 54 | -ms-overflow-style: -ms-autohiding-scrollbar; 55 | } 56 | 57 | .table-responsive.table-bordered { 58 | border: 0; 59 | } 60 | 61 | thead tr th { 62 | position: sticky; 63 | top: 0; 64 | background: #dbdbdb; 65 | color: #3f3d56; 66 | } 67 | 68 | .myMarker { 69 | position:absolute; 70 | background:rgba(255,0,0,0.5); 71 | z-index:20; 72 | } 73 | 74 | .gutterDecoration{ 75 | background:rgba(255,0,0,0.5); 76 | color:white; 77 | font-weight: bold; 78 | 79 | } 80 | 81 | .errorForeground{ 82 | position:absolute; 83 | z-index: 20; 84 | font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;; 85 | top:0; 86 | width: 20px; 87 | margin-left:-23px; 88 | text-align:center; 89 | } 90 | 91 | .errorBackground{ 92 | background: rgba(255,125,0); 93 | height: 12px; 94 | cursor: pointer; 95 | color: white; 96 | font-weight: bold; 97 | margin-left: 33px; 98 | width: 30px; 99 | position: absolute; 100 | } 101 | 102 | .actionIcon:hover { 103 | opacity: 1; 104 | transform: scale(1.3); 105 | } 106 | 107 | :root { 108 | --mdc-theme-primary: #286090; 109 | --mdc-theme-secondary: #e6e6e6; 110 | } 111 | 112 | .dir { 113 | background-color : blue; 114 | color : white; 115 | height : 20px; 116 | width : 20px; 117 | border-radius : 4px; 118 | display: flex; /* establish flex container */ 119 | flex-direction: column; /* make main axis vertical */ 120 | justify-content: center; /* center items vertically, in this case */ 121 | align-items: center; /* center items horizontally, in this case */ 122 | } 123 | 124 | .plus { 125 | text-align: center; 126 | margin: 3px; 127 | width:20px; 128 | } 129 | 130 | .col0{ 131 | flex: 0 0 30px; 132 | } 133 | 134 | .col1{ 135 | flex: 1; 136 | text-overflow: ellipsis; 137 | overflow: hidden; 138 | } 139 | 140 | .col2{ 141 | flex: 0 0 50px; 142 | font-size: 9px; 143 | justify-content: flex-end; 144 | display: flex; 145 | margin-right: 10px; 146 | } 147 | 148 | .col3{ 149 | flex: 0 0 20px; 150 | } 151 | 152 | /*TEST*/ 153 | .editor-content { 154 | flex: 1; 155 | overflow: hidden; 156 | min-height: 0; 157 | } 158 | 159 | #editor { 160 | font-family: monospace; 161 | top: 0; 162 | right: 0; 163 | bottom: 0; 164 | left: 0; 165 | } 166 | 167 | .file-content { 168 | margin: 10px; 169 | } 170 | 171 | .tab-section{ 172 | margin: 10px; 173 | flex-grow: 1; 174 | 175 | display: flex; 176 | flex-direction: column; 177 | 178 | /* for Firefox */ 179 | min-height: 0; 180 | width:100%; 181 | } 182 | 183 | .main-container { 184 | 185 | /* give the outermost container a predefined size */ 186 | top: 0; 187 | bottom: 0; 188 | left: 0; 189 | display: flex; 190 | flex-direction: row; 191 | 192 | 193 | width: 1000px; 194 | height: 500px; 195 | } 196 | 197 | .flex-row{ 198 | display: flex; 199 | flex-direction:row 200 | } 201 | 202 | .button-open { 203 | transition: width 1s ease-in-out; 204 | width: 100%; 205 | } 206 | 207 | .button-close { 208 | transition: width 1s ease-in-out; 209 | width: 0%; 210 | } 211 | 212 | .ace_gutter-cell.ace_breakpoint { 213 | background-color: red; 214 | color: white; 215 | font-weight: bold; 216 | cursor: pointer; 217 | } 218 | 219 | .mypopover{ 220 | position: absolute; 221 | z-index: 1060; 222 | min-width: 200px; 223 | min-height: 20px; 224 | max-width: 276px; 225 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 226 | font-style: normal; 227 | font-weight: 400; 228 | line-height: 1.5; 229 | text-align: left; 230 | text-align: start; 231 | text-decoration: none; 232 | text-shadow: none; 233 | text-transform: none; 234 | letter-spacing: normal; 235 | word-break: normal; 236 | word-spacing: normal; 237 | white-space: normal; 238 | line-break: auto; 239 | font-size: 0.875rem; 240 | word-wrap: break-word; 241 | background-color: #fff; 242 | background-clip: padding-box; 243 | border: 1px solid rgba(0, 0, 0, 0.2); 244 | border-radius: 0.3rem; 245 | } 246 | 247 | .mypopover-top { 248 | flex-direction: column; 249 | margin-bottom: 10px; 250 | } 251 | 252 | .left-arrow { 253 | width: 12px; 254 | height:12px; 255 | margin-top:5px; 256 | margin-left:-7px; 257 | background-color: white; 258 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 259 | border-left: 1px solid rgba(0, 0, 0, 0.2); 260 | transform: translateX(-50%) rotate(45deg); 261 | } 262 | 263 | 264 | .collapse-bottom { 265 | position: absolute; 266 | width: 100%; 267 | height: 100%; 268 | top: -40px; 269 | overflow: hidden; 270 | background-color: #000; color: #FFF; 271 | transition: all 1s; 272 | } 273 | 274 | .collapse-bottom.close { 275 | top: 100%; 276 | height: 0; 277 | } 278 | 279 | 280 | /*Switchs*/ 281 | .switch { 282 | position: relative; 283 | display: inline-block; 284 | width: 60px; 285 | height: 34px; 286 | } 287 | 288 | .switch input { 289 | opacity: 0; 290 | width: 0; 291 | height: 0; 292 | } 293 | 294 | .slider { 295 | position: absolute; 296 | cursor: pointer; 297 | top: 0; 298 | left: 0; 299 | right: 0; 300 | bottom: 0; 301 | background-color: #ccc; 302 | -webkit-transition: .4s; 303 | transition: .4s; 304 | } 305 | 306 | .slider:before { 307 | position: absolute; 308 | content: ""; 309 | height: 26px; 310 | width: 26px; 311 | left: 4px; 312 | bottom: 4px; 313 | background-color: white; 314 | -webkit-transition: .4s; 315 | transition: .4s; 316 | } 317 | 318 | input:checked + .slider { 319 | background-color: #2196F3; 320 | } 321 | 322 | input:focus + .slider { 323 | box-shadow: 0 0 1px #2196F3; 324 | } 325 | 326 | input:checked + .slider:before { 327 | -webkit-transform: translateX(26px); 328 | -ms-transform: translateX(26px); 329 | transform: translateX(26px); 330 | } 331 | 332 | /* Rounded sliders */ 333 | .slider.round { 334 | border-radius: 34px; 335 | } 336 | 337 | .slider.round:before { 338 | border-radius: 50%; 339 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/bootstrapslider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/bootstrapslider.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/flowchart.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/iscpif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/iscpif.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/modal.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/svgstar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/svgstar.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/trigger.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/resources/img/triggered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/bootstrapDemo/src/main/resources/img/triggered.png -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/AceDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 29/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import com.raquo.laminar.DomApi 21 | import scaladget.ace._ 22 | import com.raquo.laminar.api.L._ 23 | import com.raquo.laminar.nodes.ReactiveElement 24 | import org.scalajs.dom.{HTMLDivElement, MouseEvent} 25 | import scaladget.bootstrapnative.bsn._ 26 | import scaladget.bootstrapnative.Popup._ 27 | import scaladget.bootstrapnative.Tools.MyPopoverBuilder 28 | 29 | import scalajs.js 30 | 31 | object AceDemo extends Demo { 32 | 33 | 34 | val sc = sourcecode.Text { 35 | 36 | val editorHeight = "200" 37 | val lineHeight = "13" 38 | 39 | val editorDiv = div(idAttr := "editor", height := editorHeight, paddingRight := "20", zIndex := 0) 40 | 41 | val editor = ace.edit(editorDiv.ref) 42 | val session = editor.getSession() 43 | 44 | session.setBreakpoint(4) 45 | session.setBreakpoint(1) 46 | 47 | 48 | scalamode 49 | githubtheme 50 | extLanguageTools 51 | 52 | session.setMode("ace/mode/scala") 53 | editor.setTheme("ace/theme/github") 54 | 55 | session.on("changeScrollTop", (y: Any) => println("scrolled " + y)) 56 | 57 | editor.container.style.lineHeight = s"${lineHeight}px" 58 | editor.renderer.updateFontSize() 59 | 60 | session.setValue("def fib(n):\rval axx = 7\nval b = 8\nval c = a*b\n\nprintln(c)\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nAA\n\n\n\n") 61 | editor.setOptions(js.Dynamic.literal( 62 | "enableBasicAutocompletion" -> true, 63 | "enableLiveAutocompletion" -> true 64 | )) 65 | 66 | val errorMessage: Var[Option[String]] = Var(None) 67 | 68 | def buildBP = { 69 | 70 | val breakpoints = scaladget.ace.Utils.getBreakPointElements(editorDiv).toSeq 71 | breakpoints.foreach { bp => 72 | bp._1.addEventListener("click", { (e: MouseEvent) => 73 | errorMessage.update(m => 74 | m match { 75 | case None => Some("45ableBasi45ableBasicAutocompletinn45ableBasicAutocompletinn" + bp._2) 76 | case _ => None 77 | }) 78 | }) 79 | } 80 | } 81 | 82 | 83 | div( 84 | div("Youhou !", fontWeight.bold), 85 | errorMessage.signal.map { x => x.isDefined }.expand(div(child <-- errorMessage.signal.map { 86 | m=> div(m.getOrElse("")) 87 | }, backgroundColor := "orange", color := "white", height := "50")), 88 | editorDiv, 89 | button(btn_primary_outline, "Build", onClick --> { _ => buildBP }) 90 | ) 91 | 92 | } 93 | 94 | val elementDemo = new ElementDemo { 95 | def title: String = "Ace" 96 | 97 | def code: String = sc.source 98 | 99 | def element: HtmlElement = sc.value 100 | 101 | override def codeWidth: Int = 6 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/ButtonDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 23/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn.* 21 | import com.raquo.laminar.api.L.* 22 | import com.raquo.laminar.nodes.ReactiveElement.isActive 23 | 24 | object ButtonDemo { 25 | val sc = sourcecode.Text { 26 | 27 | val clicked = Var("None") 28 | 29 | val buttonStyle = Seq( 30 | marginRight := "5", 31 | marginTop := "5" 32 | ) 33 | 34 | 35 | def clickAction(tag: String) = onClick --> { _ => clicked.set(tag) } 36 | 37 | sealed trait Food 38 | object Couscous extends Food 39 | object Falafel extends Food 40 | object Pizza extends Food 41 | object Salad extends Food 42 | 43 | val yes = ToggleState("Yes", "Yes", btn_primary_string, (_: String) => println("YES")) 44 | val no = ToggleState("No", "No", btn_secondary_string, (_: String) => println("NO")) 45 | val yesOutline = ToggleState("Yes", "Yes", btn_outline_primary_string, (_: String) => println("YES")) 46 | val noOutline = ToggleState("No", "No", btn_outline_secondary_string, (_: String) => println("NO")) 47 | val male = ToggleState("Male", "Male", btn_danger_string, (_: String) => println("MALE")) 48 | val female = ToggleState("Male", "Female", btn_danger_string, (_: String) => println("FEMALE")) 49 | val pizza = ToggleState[Food](Pizza, "Pizza", btn_primary_string, (_: Food) => println("PIZZA")) 50 | val couscous = ToggleState[Food](Couscous, "Couscous", btn_primary_string, (_: Food) => println("COUSCOUS")) 51 | val falafel = ToggleState[Food](Falafel, "Falafel", btn_primary_string, (_: Food) => println("FALAFEL")) 52 | val salad = ToggleState[Food](Salad, "Salad", btn_primary_string, (_: Food) => println("SALAD")) 53 | 54 | lazy val exRadio = exclusiveRadio(Seq(male, female, ToggleState("Other", "Other", btn_danger_string, (_: String) => println("OTHER"))), btn_secondary_string, 0).element 55 | lazy val unique = toggle(yes, true, no, () => { 56 | println("toggled") 57 | }) 58 | lazy val uniqueSquared = toggle(yesOutline, true, noOutline, () => { 59 | println("toggled") 60 | }, withCaret = false) 61 | lazy val theRadio = radio[Food](Seq(pizza, couscous, falafel), Seq(pizza, couscous), btn_secondary_string) 62 | val only2 = exclusiveRadios[Food](Seq(pizza, couscous, falafel, salad), btn_secondary_string, Seq(0, 2), SelectionSize.Infinite).element 63 | div( 64 | h4("Buttons"), 65 | button("Primary", clickAction("primary"), span(glyph_edit, paddingLeft := "10"), buttonStyle, btn_primary), 66 | button("Default", clickAction("default"), buttonStyle, btn_secondary), 67 | button("Info", clickAction("info"), buttonStyle, btn_info), 68 | button("Default", clickAction("default"), buttonStyle, btn_secondary_outline), 69 | button("Primary", clickAction("primary"), buttonStyle, btn_primary_outline), 70 | button("Info", clickAction("info"), buttonStyle, btn_info_outline), 71 | button("Success", clickAction("success"), buttonStyle, btn_success), 72 | button("Warning", clickAction("warning"), buttonStyle, btn_warning), 73 | button("Danger", clickAction("danger"), buttonStyle, btn_danger), 74 | 75 | button(" Like", clickAction("danger"), buttonStyle, fontSize := "16", btn_danger, glyph_heart, clickAction("heart")), 76 | button("", clickAction("danger"), buttonStyle, fontSize := "20", btn_secondary, glyph_download, clickAction("download")), 77 | linkButton("GitHub", "https://github.com/openmole/scaladget", Seq(buttonStyle, btn_primary)), 78 | div(paddingTop := "15", child.text <-- clicked.signal.map { s => s"Clicked: ${s}" }), 79 | h4("Badges", paddingTop := "30"), 80 | button("Badge", clickAction("badge"), buttonStyle, btn_primary, badge("7", backgroundColor := "#31508c")), 81 | div("Badge", clickAction("badge"), badge("7", Seq(backgroundColor := "#31508c", color := "white"))), 82 | h4("Icon buttons", paddingTop := "30"), 83 | glyphSpan(Seq(glyph_refresh, buttonStyle, fontSize := "20"), clickAction("refresh")), 84 | glyphSpan(Seq(glyph_lightning, buttonStyle, fontSize := "20"), clickAction("flash")), 85 | h4("Radio", paddingTop := "30"), 86 | unique.element, 87 | uniqueSquared.element.amend(width := "50", height := "50", marginLeft := "20"), 88 | h4("2 exclusive radio buttons", paddingTop := "30"), 89 | only2, 90 | h4("Exclusive radio buttons", paddingTop := "30"), 91 | exRadio, 92 | h4("Toggle buttons", paddingTop := "30"), 93 | theRadio, 94 | h4("Check button"), 95 | checkbox(true), 96 | h4("Switch"), 97 | div(display.flex, flexDirection.row, alignItems.center, 98 | div("My switch", height := "34px", marginRight := "10px"), 99 | label(cls := "switch", 100 | input(`type` := "checkbox", checked := true), 101 | span(cls := "slider round" 102 | ) 103 | ) 104 | ) 105 | ) 106 | } 107 | 108 | val elementDemo = new ElementDemo { 109 | def title: String = "Button" 110 | 111 | def code: String = sc.source 112 | 113 | def element: HtmlElement = sc.value 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/CollapseDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import scaladget.bootstrapnative.bsn._ 4 | import com.raquo.laminar.api.L._ 5 | 6 | /* 7 | * Copyright (C) 30/08/16 // mathieu.leclaire@openmole.org 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | object CollapseDemo extends Demo { 24 | 25 | 26 | val sc = sourcecode.Text { 27 | 28 | val onoff: Var[Option[Int]] = Var(None) 29 | val onoffBottom: Var[Boolean] = Var(false) 30 | 31 | def onoffUpdate(currentId: Option[Int], id: Int) = { 32 | if (Some(id) == currentId) None 33 | else Some(id) 34 | } 35 | 36 | def expandAction(i: Option[Int], butId: Int) = { 37 | i match { 38 | case Some(ii: Int) => 39 | if (ii == butId) true 40 | else false 41 | case None => false 42 | } 43 | } 44 | 45 | div( 46 | button(" Trigger !", btn_primary, marginBottom := "10", glyph_settings).expandOnclick( 47 | div(child.text <-- onoff.signal.map(oo => "My text in detail " + oo)).amend(width := "400", height := "200") 48 | ), 49 | button("Set Var", btn_danger, onClick --> { _ => onoff.update(i => onoffUpdate(i, 1)) }), 50 | onoff.signal.map(oo => expandAction(oo, 1)).expand(div("Yes", backgroundColor := "orange", height := "150")), 51 | button("Set Var", btn_danger, onClick --> { _ => onoff.update(i => onoffUpdate(i, 2)) }), 52 | onoff.signal.map(oo => expandAction(oo, 2)).expand(div("Yes", backgroundColor := "orange", height := "150")), 53 | button("Set Var", btn_danger, onClick --> { _ => onoffBottom.update(!_) }), 54 | div("Yes", backgroundColor := "orange", cls <-- onoffBottom.signal.map{oo=> if (oo) "collapse-bottom" else "collapse-bottom close"}) 55 | ) 56 | } 57 | 58 | val elementDemo = new ElementDemo { 59 | def title: String = "Collapse" 60 | 61 | def code: String = sc.source 62 | 63 | def element: HtmlElement = sc.value 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/Demo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import com.raquo.laminar.api.L._ 4 | 5 | /* 6 | * Copyright (C) 19/08/16 // mathieu.leclaire@openmole.org 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | 23 | trait Demo{ 24 | def elementDemo: ElementDemo 25 | } 26 | 27 | trait ElementDemo{ 28 | def title: String 29 | 30 | def code: String 31 | 32 | def cleanCode = { 33 | if (code.startsWith("{")) code.tail.dropRight(1) 34 | else code 35 | } 36 | def element: HtmlElement 37 | 38 | def codeWidth: Int = 8 39 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/FormDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 24/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn._ 21 | import com.raquo.laminar.api.L._ 22 | 23 | 24 | object FormDemo extends Demo { 25 | 26 | val sc = sourcecode.Text { 27 | 28 | case class MyElement(name: String) 29 | 30 | val inputStyle = width := "150" 31 | val loginValue = Var("Mathieu") 32 | 33 | val elements = Seq( 34 | MyElement("Male"), 35 | MyElement("Female") 36 | ) 37 | 38 | val loginInput = inputTag(loginValue.now()).amend( 39 | placeholder := "Login", 40 | width := "150", 41 | inContext { thisNode => onInput.map(_ => thisNode.ref.value) --> loginValue } 42 | ) 43 | 44 | 45 | val passInput = inputTag("").amend(placeholder := "Password", `type` := "password", inputStyle) 46 | val cityInput = inputTag("").amend(placeholder := "City", inputStyle) 47 | 48 | val genderDD = elements.options(1, btn_success, (m: MyElement) => m.name, decorations = Map(elements(0) -> glyph_settings)).selector 49 | 50 | div(display.flex, flexDirection.column, 51 | vForm(width := "200", 52 | loginInput.withLabel("Login"), 53 | passInput.withLabel("Password") 54 | ), 55 | hForm(Seq(paddingTop := "20", width := "500"), 56 | cityInput.withLabel("City"), 57 | genderDD 58 | ), 59 | stickBefore(loginInput, inputGroupText("@")), 60 | stickAfter(passInput, button("OK", btn_primary)).amend(paddingTop := "20"), 61 | span(marginTop := "20", child.text <-- loginValue.signal.map { lv => s"Login : $lv" }) 62 | ) 63 | 64 | 65 | } 66 | 67 | val elementDemo = new ElementDemo { 68 | def title: String = "Form" 69 | 70 | def code: String = sc.source 71 | 72 | def element: HtmlElement = sc.value 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/LabelDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalajs.dom._ 4 | 5 | /* 6 | * Copyright (C) 24/08/16 // mathieu.leclaire@openmole.org 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | import org.scalajs.dom.Element 22 | 23 | import scaladget.bootstrapnative.bsn._ 24 | import com.raquo.laminar.api.L._ 25 | 26 | object LabelDemo { 27 | val sc = sourcecode.Text { 28 | 29 | val hovered = Var("None") 30 | val spanStyle: HESetters = Seq( 31 | marginTop := "20" 32 | ) 33 | 34 | def overAction(tag: String) = onMouseOver --> { _ => hovered.set(tag) } 35 | 36 | div( 37 | span("Light", badge_light, overAction("light")).size4(spanStyle), 38 | span("Primary", badge_primary, overAction("primary")).size4(spanStyle), 39 | span("Info", badge_info, overAction("info")).size4(spanStyle), 40 | span("Success", badge_success, overAction("success")).size4(spanStyle), 41 | span("Warning", badge_warning, overAction("warning")).size5(spanStyle), 42 | span("Danger", badge_danger, overAction("danger")).size6(spanStyle), 43 | div(paddingTop := "15", child.text <-- hovered.signal.map { s => s"Hovered: $s" }) 44 | 45 | ) 46 | } 47 | 48 | val elementDemo = new ElementDemo { 49 | def title: String = "Label" 50 | 51 | def code: String = sc.source 52 | 53 | def element: HtmlElement = sc.value 54 | } 55 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/ModalDialogDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | 4 | /* 5 | * Copyright (C) 19/08/16 // mathieu.leclaire@openmole.org 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import scaladget.bootstrapnative.bsn._ 22 | import com.raquo.laminar.api.L._ 23 | 24 | object ModalDialogDemo extends Demo { 25 | 26 | val sc = sourcecode.Text { 27 | 28 | def dialogCloseTrigger = button( 29 | "Close", 30 | btn_secondary, 31 | onClick --> { _ => 32 | dialog.hide 33 | } 34 | ) 35 | 36 | lazy val dialog: ModalDialog = ModalDialog( 37 | div("Header"), 38 | div("body"), 39 | div(btnGroup, dialogCloseTrigger, button("OK", btn_success)), 40 | onopen = () => println("Opened"), 41 | onclose = () => println("closed") 42 | ) 43 | 44 | def openAction = onClick --> { _ => dialog.show } 45 | 46 | val dialogTrigger = button( 47 | btn_primary, 48 | "Modal !", 49 | openAction 50 | ) 51 | 52 | span( 53 | dialog.render, 54 | dialogTrigger, 55 | span(glyph_settings, paddingLeft := "5", cursor.pointer, openAction) 56 | ) 57 | } 58 | 59 | 60 | val elementDemo = new ElementDemo { 61 | def title: String = "Modal" 62 | 63 | def code: String = sc.source 64 | 65 | def element: HtmlElement = sc.value 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/NavBarDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 22/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | import scaladget.bootstrapnative.bsn._ 22 | import com.raquo.laminar.api.L._ 23 | 24 | 25 | object NavBarDemo { 26 | val sc = sourcecode.Text { 27 | 28 | // Create nav items 29 | val oneItem = stringNavItem("One", () ⇒ 30 | println("One open") 31 | ) 32 | 33 | val twoItem = stringNavItem("Two", () ⇒ 34 | println("Two open"), true 35 | ) 36 | 37 | val threeItem = navItem( 38 | form( 39 | formInline, 40 | inputTag("").amend(placeholder := "Name", width := "100"), 41 | button("Search", btn_success_outline, onClick --> { _ => 42 | println("Five open") 43 | }) 44 | ), 45 | alignRight = true 46 | ) 47 | 48 | //Create the nav bar 49 | navBar( 50 | Seq(navbar_light, backgroundColor := "#23ed12", bg_light), 51 | oneItem, 52 | twoItem, 53 | threeItem, 54 | ).withBrand("img/iscpif.png", width := "80", () => println("Brand")).render 55 | } 56 | 57 | val elementDemo = new ElementDemo { 58 | def title: String = "Nav bar" 59 | 60 | def code: String = sc.source 61 | 62 | def element: HtmlElement = sc.value 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/PlayGround.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 23/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn._ 21 | import com.raquo.laminar.api.L._ 22 | 23 | 24 | object PlayGround { 25 | val sc = sourcecode.Text { 26 | 27 | val onoff = Var(-1) 28 | 29 | def fileTable(rows: Seq[(Seq[HtmlElement], Int)]) = { 30 | div( 31 | rows.map { case (r, id) ⇒ 32 | div(display.flex, flexDirection.column, width := "200px", 33 | div(display.flex, alignItems.center, 34 | r.zipWithIndex.map { case (e, c) ⇒ 35 | e.amend(cls := s"col$c") 36 | } 37 | ), 38 | onoff.signal.map { i => i == id }.expand(div(s"Yes $id", backgroundColor := "orange", height := "50")) 39 | ) 40 | } 41 | ) 42 | } 43 | 44 | def dirBox = { 45 | div(cls := "dir", 46 | div(cls := "plus bi-plus") 47 | ) 48 | } 49 | 50 | 51 | def gear(id: Int) = div(cls := "bi-gear-fill", cursor.pointer, onClick --> { _ => 52 | onoff.set( 53 | if (id == onoff.now()) -1 54 | else id 55 | ) 56 | }) 57 | 58 | val dirs = Seq("one", "two", "trestrestrestrestrestrestrestrestrestreslong") 59 | 60 | 61 | //tagging buttons 62 | type TagBadge = Span 63 | val tags: Var[Seq[TagBadge]] = Var(Seq()) 64 | 65 | def buildTag(text: String) = { 66 | span(text, 67 | badge_secondary, margin := "3px", fontSize := "14px", padding := "10px", 68 | inContext(thisNode => 69 | span(cls := "close-button bi-x", paddingLeft := "10", cursor.pointer, 70 | onClick --> { _ => tags.update { t => t.filterNot(_ == thisNode) } }) 71 | ) 72 | ) 73 | } 74 | 75 | val tagTextInput = inputTag("").amend(onMountFocus, placeholder := "your tag here") 76 | 77 | val tagPanel = div( marginTop := "30px", 78 | form(tagTextInput, onSubmit.preventDefault --> { _ => 79 | tags.update { ts => ts :+ buildTag(tagTextInput.ref.value) } 80 | tagTextInput.ref.value = "" 81 | }), 82 | div(display.flex, flexDirection.row, flexWrap.wrap, width := "500px", children <-- tags.signal) 83 | ) 84 | 85 | div( 86 | fileTable(dirs.zipWithIndex.map { case (d, id) => 87 | (Seq(div(dirBox), div(d), div("4.00KB"), gear(id)), id) 88 | }), 89 | tagPanel 90 | ) 91 | } 92 | 93 | val elementDemo = new ElementDemo { 94 | def title: String = "Playground" 95 | 96 | def code: String = sc.source 97 | 98 | def element: HtmlElement = sc.value 99 | } 100 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/PopoverDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 24/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | import scaladget.bootstrapnative.bsn._ 22 | import com.raquo.laminar.api.L._ 23 | 24 | object PopoverDemo extends Demo { 25 | val sc = sourcecode.Text { 26 | 27 | import scaladget.bootstrapnative.Popup._ 28 | 29 | val buttonStyle: HESetters = Seq( 30 | btn_secondary, 31 | marginRight := "5" 32 | ) 33 | 34 | val open = Var(false) 35 | 36 | //SIMPLE POPOVERS 37 | lazy val simplePopovers = div( 38 | h2("Simple popovers"), 39 | div(paddingTop := "20", "Simple popovers containing text, or simple content with no events to be fired and with basic trigger modes (click, hover)."), 40 | // button("Left", buttonStyle).popover(vForm(width := 100)(label("Nice content", label_danger).render, span("A important message").render), Left, title = Some("Check this !")).render, 41 | button("Title", buttonStyle).popover(div(button(btn_primary, "Hey"), div("Popover on hover with Title")), Top, ClickPopup, title = Some("Pop title")).render, 42 | button("Title 2", buttonStyle).popover(div("Popover on hover with Title", color := "red"), Top, ClickPopup, title = Some("Pop title")).render, 43 | button("Dismissable", buttonStyle).popover(div("An other popover"), Top, HoverPopup, Some("Pop title"), true).render, 44 | inputTag("").amend(width := "320", marginTop := "10", placeholder := "Bottom (click)").popover(div("Tooltip on click on bottom"), Bottom, ClickPopup).render, 45 | div(cls := "flex-row", justifyContent.right, 46 | div( 47 | cls <-- open.signal.map { o => 48 | if (o) "button-open" else "button-close" 49 | }, 50 | inputTag("blablaba").amend(onSubmit --> { _ => open.update(!_) }) 51 | ), 52 | button("Open", buttonStyle, onClick --> { _ => open.update(!_) }) 53 | ) 54 | ) 55 | 56 | simplePopovers 57 | } 58 | 59 | val elementDemo = new ElementDemo { 60 | def title: String = "Popover" 61 | 62 | def code: String = sc.source 63 | 64 | def element: HtmlElement = sc.value 65 | 66 | override def codeWidth: Int = 9 67 | } 68 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/SelectDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | 4 | import scaladget.bootstrapnative.bsn._ 5 | import com.raquo.laminar.api.L._ 6 | 7 | 8 | /* 9 | * Copyright (C) 22/08/16 // mathieu.leclaire@openmole.org 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | object SelectDemo { 26 | 27 | val sc = sourcecode.Text { 28 | import scaladget.bootstrapnative.Selector._ 29 | 30 | // Define a toy case class containing at least a name attribute 31 | case class MyElement(name: String) 32 | 33 | // Define the option sequence 34 | val first = MyElement("First element") 35 | val second = MyElement("Second Element") 36 | val third = MyElement("Third Element") 37 | val elements = Seq(first, second, third) 38 | 39 | 40 | val selected: Var[MyElement] = Var(elements(1)) 41 | 42 | lazy val optionDropDown: Options[MyElement] = 43 | elements.options( 44 | 1, 45 | Seq(btn_success, width := "160"), 46 | (m: MyElement) => m.name, 47 | onclose = () => selected.set(optionDropDown.content.now().get), 48 | decorations = Map(first -> glyph_archive, second -> glyph_settings, third -> glyph_filter) 49 | ) 50 | 51 | lazy val fixedTitleOptions: Options[MyElement] = elements.options( 52 | key = btn_danger, 53 | naming = (m: MyElement) => m.name, 54 | onclose = () => println(fixedTitleOptions.content.now().get), 55 | fixedTitle = Some("Actions") 56 | ) 57 | 58 | 59 | hForm(padding := "5", 60 | optionDropDown.selector, 61 | fixedTitleOptions.selector, 62 | div( 63 | padding := "8", 64 | child.text <-- selected.signal.map(s => s"Selected:${s.name}") 65 | ) 66 | ) 67 | 68 | } 69 | 70 | 71 | val elementDemo = new ElementDemo { 72 | def title: String = "Dropdown" 73 | 74 | def code: String = sc.source 75 | 76 | def element: HtmlElement = sc.value 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/SliderDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 23/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import com.raquo.laminar.api.L._ 21 | import scaladget.nouislider._ 22 | import scaladget.nouislider.NoUISliderImplicits._ 23 | import scala.scalajs.js 24 | 25 | object SliderDemo { 26 | val sc = sourcecode.Text { 27 | 28 | val elStyle = margin := "60 0 60 0" 29 | val el1 = div(elStyle) 30 | val el2 = div(elStyle) 31 | val el3 = div(elStyle, height := "150", idAttr := "s3") 32 | 33 | noUiSlider.create( 34 | el1.ref, Options 35 | .range(Range.min(1300).max(3250)) 36 | .start(1450) 37 | .step(100) 38 | .connect(Options.Lower) 39 | .tooltips(true) 40 | ) 41 | 42 | noUiSlider.create( 43 | el2.ref, Options 44 | .range(Range.min(1300.45).max(3250.5)) 45 | .start(js.Array(1450.0, 1800.0)) 46 | .connect(js.Array(true, false, true)) 47 | .tooltips(js.Array(true, true)) 48 | ) 49 | 50 | noUiSlider.create( 51 | el3.ref, Options 52 | .range(Range.min(0).max(10)) 53 | .start(5) 54 | .connect(Options.Lower) 55 | .orientation(Options.Vertical) 56 | .tooltips(true) 57 | ) 58 | 59 | el2.noUiSlider.on(event.ChangeEvent, (value, handle) => println("val : " + value + " on " + handle )) 60 | 61 | el2.ref.noUiSlider.set(js.Array(2000.0, 2500.0)) 62 | 63 | println("2 " + el2.noUiSlider.get()) 64 | 65 | 66 | div(el1, el2, el3) 67 | } 68 | 69 | val elementDemo = new ElementDemo { 70 | def title: String = "Slider" 71 | 72 | def code: String = sc.source 73 | 74 | def element: HtmlElement = sc.value 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/StartDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | 4 | import scaladget.highlightjs.HighlightJS 5 | import scaladget.bootstrapnative.bsn._ 6 | import com.raquo.laminar.api.L.{*, given} 7 | import org.scalajs 8 | 9 | 10 | /* 11 | * Copyright (C) 24/03/16 // mathieu.leclaire@openmole.org 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU Affero General Public License as published by 15 | * the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU Affero General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License 24 | */ 25 | 26 | object App { 27 | 28 | def main(args: Array[String]): Unit = { 29 | 30 | 31 | scaladget.highlightjs.scalamode 32 | HighlightJS.initHighlightingOnLoad() 33 | 34 | def imports = 35 | """ 36 | import scaladget.bootstrapnative.bsn._ 37 | import scaladget.tools._ 38 | import scalatags.JsDom.all._ 39 | """.stripMargin 40 | 41 | // val element = AceDemo.elementDemo 42 | // println("Element " + element) 43 | lazy val content = { 44 | div(containerFluid, marginLeft := "15", marginTop := "25", 45 | h3("Build"), 46 | div(row, 47 | div(colSM, "Details on construction on ", a(href := "https://github.com/openmole/scaladget", target := "_blank", "the Scaladet Github page")) 48 | ), 49 | h3("Imports"), 50 | div(row, 51 | div(colSM, pre(code(cls("scala"), imports))), 52 | div(colSM, padding := "20", "This imports have to be done before using the following examples. Specific imports will be also sometimes specified.") 53 | ), 54 | // FormDemo.elementDemo.element 55 | Tabs.tabs( 56 | for { 57 | demo <- Seq( 58 | LabelDemo.elementDemo, 59 | ButtonDemo.elementDemo, 60 | ModalDialogDemo.elementDemo, 61 | CollapseDemo.elementDemo, 62 | FormDemo.elementDemo, 63 | SelectDemo.elementDemo, 64 | PopoverDemo.elementDemo, 65 | TabDemo.elementDemo, 66 | TooltipDemo.elementDemo, 67 | NavBarDemo.elementDemo, 68 | ToastDemo.elementDemo, 69 | TableDemo.elementDemo, 70 | SliderDemo.elementDemo, 71 | AceDemo.elementDemo, 72 | PlayGround.elementDemo, 73 | ) 74 | } yield { 75 | Tab(demo, span(demo.title), 76 | div( 77 | h3(demo.title), 78 | div(containerFluid, 79 | div(row, marginLeft := "15", marginTop := "25", 80 | div(colBS(demo.codeWidth), pre(code(cls := "scala", demo.cleanCode))), 81 | div(colBS(12 - demo.codeWidth), demo.element) 82 | ) 83 | ) 84 | ) 85 | ) 86 | }, 87 | tabStyle = navbar_pills).build.render 88 | ) 89 | 90 | } 91 | 92 | renderOnDomContentLoaded(scalajs.dom.document.body, content) 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/TabDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 05/01/17 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn._ 21 | import com.raquo.laminar.api.L._ 22 | import scaladget.ace.{ace, extLanguageTools, githubtheme, scalamode} 23 | 24 | import scala.scalajs.js 25 | 26 | object TabDemo extends Demo { 27 | val sc = sourcecode.Text { 28 | 29 | case class AA(i: Int) 30 | 31 | val rng = scala.util.Random 32 | 33 | lazy val trigger = button("Add tab", onClick --> { _ => { 34 | val aa = AA(rng.nextInt()) 35 | theTabs.add(Tab(aa, span("An other"), div(s"Random number: ${aa.i}"))) 36 | } 37 | }, btn_danger, marginBottom := "20") 38 | 39 | lazy val theTabs = Tabs.tabs(isClosable = true). 40 | add(Tab(AA(4), span("My first"), div("My div content"), onClicked = () => println("My first clicked"))). 41 | add(Tab(AA(44), span("My long"), div("Another div content"), onClicked = () => println("bla"))). 42 | add(Tab(AA(5), span("My second"), inputTag("Hey !"), onRemoved = () => println("5 is dead"))).build 43 | 44 | 45 | val toaster = toastStack(bottomRightPosition, unstackOnClose = true) 46 | val warningToast = toast(ToastHeader("Warning", backgroundColor = "#eee"), "Your tab is empty", delay = Some(2000)) 47 | 48 | lazy val tabsObserver = Observer[Seq[Tab[AA]]] { (aas: Seq[Tab[AA]]) => 49 | if (aas.isEmpty) toaster.stackAndShow(warningToast) 50 | } 51 | 52 | div( 53 | trigger, 54 | div(cls := "main-container", 55 | theTabs.render("file-content editor-content").amend(cls := "tab-section"), 56 | theTabs.tabs --> tabsObserver, 57 | toaster.render 58 | ) 59 | ) 60 | } 61 | 62 | 63 | val elementDemo = new ElementDemo { 64 | def title: String = "Tab" 65 | 66 | def code: String = sc.source 67 | 68 | def element: HtmlElement = sc.value 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/TableDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 05/01/17 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn._ 21 | import com.raquo.laminar.api.L._ 22 | import scaladget.bootstrapnative.Table.{BSTableStyle, BasicRow} 23 | import scaladget.tools.Utils._ 24 | 25 | object TableDemo extends Demo { 26 | val sc = sourcecode.Text { 27 | 28 | val rowFlex = Seq(display.flex, flexDirection.row, justifyContent.spaceAround, alignItems.center) 29 | val columnFlex = Seq(display.flex, flexDirection.column, justifyContent.flexStart) 30 | 31 | 32 | val triggerSignal = Var(false) 33 | val subTrigger = button(btn_primary, "Expand", onClick --> { _ => triggerSignal.update(!_) }) 34 | val row = BasicRow(Seq(span("0.5"), span("0.01"), subTrigger)) 35 | 36 | val triggerSignal2 = Var(false) 37 | val subTrigger2 = button(btn_primary, "Expand", onClick --> { _ => triggerSignal2.update(!_) }) 38 | val row2 = BasicRow(Seq(span("0.5"), span("0.01"), subTrigger2)) 39 | 40 | val subContent = div( 41 | height := "80", rowFlex, paddingLeft := "20", 42 | "I am a sub", 43 | input().amend(placeholder := "Your text here") 44 | ) 45 | 46 | val subContent2 = div(height := "80", padding := "15", 47 | exclusiveRadio( 48 | Seq(ToggleState("One", "One",btn_primary_string, (_:String)=> println("ONE")), ToggleState("Two", "Two", btn_primary_string, (_:String)=> println("TWO")), ToggleState("Three", "Three", btn_primary_string, (_:String)=> println("THREE"))), 49 | btn_secondary_string, 50 | 1 51 | ).element 52 | ) 53 | 54 | lazy val tableWithSubs = elementTable() 55 | .addHeaders("First", "Second", "Actions") 56 | .addRow(span("0.0"), span("0.99"), span("0.88")) 57 | .addRow(row).expandTo(subContent, triggerSignal.signal) 58 | .addRow(span("0.0"), span("0.99"), span("0.88")) 59 | .addRow(span("0.0"), span("0.99"), span("0.88")) 60 | .addRow(row2).expandTo(subContent2, triggerSignal2.signal) 61 | .addRow(span("0.0"), span("0.99"), span("0.88")) 62 | .addRow(span("0.0"), span("0.99"), span("0.88")) 63 | .render 64 | 65 | 66 | lazy val tableWithData = dataTable() 67 | .addHeaders("Data 1", "Data 2", "Data 3") 68 | .addRow("0.0", "0.103", "88.6") 69 | .addRow("500", "153", "486") 70 | .addRow("7.0", "3", "77.56") 71 | .addRow("309", "3.14", "2218") 72 | .style(striped_table) 73 | .sortable 74 | .render 75 | 76 | lazy val tableWithDataDark = dataTable() 77 | .addHeaders("Data 1", "Data 2", "Data 3") 78 | .addRow("0.0", "0.103", "88.6") 79 | .addRow("309", "3.14", "2218") 80 | .addRow("0.0", "0.103", "88.6") 81 | .addRow("309", "3.14", "2218") 82 | .addRow("0.0", "0.103", "88.6") 83 | .addRow("309", "3.14", "2218") 84 | .addRow("0.0", "0.103", "88.6") 85 | .addRow("309", "3.14", "2218") 86 | .addRow("0.0", "0.103", "88.6") 87 | .addRow("309", "3.14", "2218") 88 | .addRow("0.0", "0.103", "88.6") 89 | .addRow("309", "3.14", "2218") 90 | .addRow("0.0", "0.103", "88.6") 91 | .addRow("309", "3.14", "2218") 92 | .addRow("0.0", "0.103", "88.6") 93 | .addRow("309", "3.14", "2218") 94 | .addRow("0.0", "0.103", "88.6") 95 | .addRow("309", "3.14", "2218") 96 | .style(inverse_table) 97 | .sortable 98 | .render 99 | 100 | 101 | lazy val filterInput = inputTag("").amend(marginBottom := "20", 102 | inContext { context => 103 | onInput --> { _ => 104 | tableWithData.setFilter(context.ref.value) 105 | } 106 | } 107 | ) 108 | 109 | div( 110 | tableWithSubs.render, 111 | filterInput, 112 | tableWithData.render, 113 | div(height := "200px", overflow.auto, tableWithDataDark.render) 114 | ) 115 | 116 | } 117 | 118 | 119 | val elementDemo = new ElementDemo { 120 | def title: String = "Table" 121 | 122 | def code: String = sc.source 123 | 124 | def element: HtmlElement = sc.value 125 | 126 | override def codeWidth: Int = 6 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/ToastDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import org.scalajs.dom._ 4 | 5 | /* 6 | * Copyright (C) 24/08/16 // mathieu.leclaire@openmole.org 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | import org.scalajs.dom.Element 22 | 23 | import scaladget.bootstrapnative.bsn._ 24 | import com.raquo.laminar.api.L._ 25 | 26 | object ToastDemo { 27 | val sc = sourcecode.Text { 28 | 29 | val myToast = toast(ToastHeader("My header", backgroundColor = "#ffc107"), "My important message", delay = Some(2000)) 30 | val mySecondToast = toast(ToastHeader("My second header", backgroundColor = "#dc3545"), "My important message") 31 | val myThirdToast = toast(ToastHeader("My third header", backgroundColor = "#17a2b8"), "My important message") 32 | val toaster = toastStack(bottomRightPosition, unstackOnClose = true) 33 | 34 | div( 35 | button(btn_warning_outline, "Toast with delay", onClick --> { _ => toaster.stackAndShow(myToast) }), 36 | button(btn_danger_outline, marginLeft := "15", "Toast again !", onClick --> { _ => toaster.stackAndShow(mySecondToast) }), 37 | button(btn_info_outline, marginLeft := "15", "Toast again !", onClick --> { _ => toaster.stackAndShow(myThirdToast) }), 38 | toaster.render 39 | ) 40 | } 41 | 42 | val elementDemo = new ElementDemo { 43 | def title: String = "Toast" 44 | 45 | def code: String = sc.source 46 | 47 | def element: HtmlElement = sc.value 48 | } 49 | } -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/TooltipDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 23/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn._ 21 | import com.raquo.laminar.api.L._ 22 | 23 | object TooltipDemo extends Demo { 24 | val sc = sourcecode.Text { 25 | 26 | import scaladget.bootstrapnative.Popup._ 27 | 28 | div( 29 | button("Left", btn_secondary, marginRight := "5").tooltip("Tooltip on left", Left), 30 | label("Right", badge_primary, marginRight := "5").tooltip("Tooltip on right", Right), 31 | label("Top", badge_success, marginRight := "5").tooltip("Tooltip on top", Top), 32 | button("Bottom", btn_info_outline).tooltip("Tooltip on bottom", Bottom) 33 | ) 34 | } 35 | 36 | val elementDemo = new ElementDemo { 37 | def title: String = "Tooltip" 38 | 39 | def code: String = sc.source 40 | 41 | def element: HtmlElement = sc.value 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bootstrapDemo/src/main/scala/demo/Utils.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | /* 3 | * Copyright (C) 24/03/16 // mathieu.leclaire@openmole.org 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | */ 17 | 18 | import scala.scalajs.js.JSConverters._ 19 | object Utils { 20 | 21 | lazy val rng = scala.util.Random 22 | 23 | val viridis = Seq( 24 | "440154", 25 | "481567", 26 | "482677", 27 | "453781", 28 | "404788", 29 | "39568C", 30 | "33638D", 31 | "2D708E", 32 | "287D8E", 33 | "238A8D", 34 | "1F968B", 35 | "20A387", 36 | "29AF7F", 37 | "3CBB75", 38 | "55C667", 39 | "73D055", 40 | "95D840", 41 | "B8DE29", 42 | "DCE319", 43 | "FDE725" 44 | ) 45 | 46 | def randomDoubles(nb: Int = 100, ratio: Int = 1000) = Seq.fill(nb)(rng.nextDouble * ratio).toJSArray 47 | 48 | def randomDoublesAsStrings(nb: Int = 100, ratio: Int = 1000) = Seq.fill(nb)((rng.nextDouble * ratio).toString).toJSArray 49 | 50 | def randomInts(nb: Int = 100, ratio: Int = 1000) = Seq.fill(nb)(rng.nextInt(ratio)).toJSArray 51 | 52 | def anArray = Array( 53 | 4.9, 54 | 4.7, 55 | 4.6, 56 | 5.0, 57 | 5.4, 58 | 4.6, 59 | 5.0, 60 | 4.4, 61 | 4.9, 62 | 5.4, 63 | 4.8, 64 | 4.8, 65 | 4.3, 66 | 5.8, 67 | 5.7, 68 | 5.4, 69 | 5.1, 70 | 5.7, 71 | 5.1, 72 | 5.4, 73 | 5.1, 74 | 4.6, 75 | 5.1, 76 | 4.8, 77 | 5.0, 78 | 5.0, 79 | 5.2, 80 | 5.2, 81 | 4.7, 82 | 4.8, 83 | 5.4, 84 | 5.2, 85 | 5.5, 86 | 4.9, 87 | 5.0, 88 | 5.5, 89 | 4.9, 90 | 4.4, 91 | 5.1, 92 | 5.0, 93 | 4.5, 94 | 4.4, 95 | 5.0, 96 | 5.1, 97 | 4.8, 98 | 5.1, 99 | 4.6, 100 | 5.3, 101 | 5.0, 102 | 7.0, 103 | 6.4, 104 | 6.9, 105 | 5.5, 106 | 6.5, 107 | 5.7, 108 | 6.3, 109 | 4.9, 110 | 6.6, 111 | 5.2, 112 | 5.0, 113 | 5.9, 114 | 6.0, 115 | 6.1, 116 | 5.6, 117 | 6.7, 118 | 5.6, 119 | 5.8, 120 | 6.2, 121 | 5.6, 122 | 5.9, 123 | 6.1, 124 | 6.3, 125 | 6.1, 126 | 6.4, 127 | 6.6, 128 | 6.8, 129 | 6.7, 130 | 6.0, 131 | 5.7, 132 | 5.5, 133 | 5.5, 134 | 5.8, 135 | 6.0, 136 | 5.4, 137 | 6.0, 138 | 6.7, 139 | 6.3, 140 | 5.6, 141 | 5.5, 142 | 5.5, 143 | 6.1, 144 | 5.8, 145 | 5.0, 146 | 5.6, 147 | 5.7, 148 | 5.7, 149 | 6.2, 150 | 5.1, 151 | 5.7, 152 | 6.3, 153 | 5.8, 154 | 7.1, 155 | 6.3, 156 | 6.5, 157 | 7.6, 158 | 4.9, 159 | 7.3, 160 | 6.7, 161 | 7.2, 162 | 6.5, 163 | 6.4, 164 | 6.8, 165 | 5.7, 166 | 5.8, 167 | 6.4, 168 | 6.5, 169 | 7.7, 170 | 7.7, 171 | 6.0, 172 | 6.9, 173 | 5.6, 174 | 7.7, 175 | 6.3, 176 | 6.7, 177 | 7.2, 178 | 6.2, 179 | 6.1, 180 | 6.4, 181 | 7.2, 182 | 7.4, 183 | 7.9, 184 | 6.4, 185 | 6.3, 186 | 6.1, 187 | 7.7, 188 | 6.3, 189 | 6.4, 190 | 6.0, 191 | 6.9, 192 | 6.7, 193 | 6.9, 194 | 5.8, 195 | 6.8, 196 | 6.7, 197 | 6.7, 198 | 6.3, 199 | 6.5, 200 | 6.2, 201 | 5.9).toJSArray 202 | } 203 | -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/BootstrapTags.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | /* 4 | * Copyright (C) 27/05/15 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | //import net.scalapro.sortable.{EventS, Sortable, SortableOptions} 21 | 22 | import scaladget.bootstrapnative.Popup.{Bottom, ClickPopup, HoverPopup, Manual, PopupPosition, PopupType} 23 | import scaladget.tools.Utils.* 24 | import com.raquo.laminar.api.L.{*, given} 25 | import bsn.spacing.* 26 | import scaladget.bootstrapnative 27 | import scaladget.bootstrapnative.Table.{DataContent, DataTableBuilder, ElementTableBuilder, Row} 28 | 29 | import scalajs.js.| 30 | 31 | object BootstrapTags extends BootstrapTags 32 | 33 | trait BootstrapTags { 34 | bstags => 35 | 36 | type HESetter = Setter[HtmlElement] 37 | 38 | type HESetters = Seq[HESetter] 39 | 40 | implicit def HESetterToHeSetters(setter: HESetter): HESetters = Seq(setter) 41 | 42 | val emptySetters: HESetters = Seq[HESetter]() 43 | 44 | def inputTag(content: String = "") = input(bsn.formControl, value := content) 45 | 46 | // implicit def inputGroupToDiv(inputGroup: InputGroup): HtmlElement = 47 | // inputGroupBS.amend( 48 | // inputGroupAppend(inputGroup.app) 49 | // //.amend(inputGroupPrepend(inputGroup.pre)) 50 | // .amend(inputGroup.el) 51 | // 52 | // ) 53 | // 54 | // case class InputGroup(pre: HtmlElement = div(), el: HtmlElement = div(), app: HtmlElement = div()) { 55 | // def prepend(preElement: HtmlElement)= copy(pre = preElement) 56 | // 57 | // def element(el: HtmlElement) = copy(el = el) 58 | // 59 | // def append(appElement: HtmlElement) = copy(appElement) 60 | // } 61 | // 62 | // val inputGroup = InputGroup() 63 | 64 | def stickBefore(element: HtmlElement, withSticker: HtmlElement) = 65 | div( 66 | bsnsheet.inputGroupClass, 67 | inputGroupPrepend(withSticker), 68 | element 69 | ) 70 | 71 | def stickAfter(element: HtmlElement, withSticker: HtmlElement) = 72 | div( 73 | bsnsheet.inputGroupClass, 74 | element, 75 | inputGroupAppend(withSticker), 76 | ) 77 | 78 | def inputGroupPrepend(element: HtmlElement) = span(cls("input-group-prepend")).amend(element) 79 | 80 | def inputGroupAppend(element: HtmlElement) = span(cls("input-group-append")).amend(element) 81 | 82 | def inputGroupText(text: String) = span(cls("input-group-text"), text) 83 | 84 | //val input_group_lg = "input-group-lg" 85 | 86 | // for multiple files, add multiple := true attr 87 | def fileInput(todo: Input => Unit) = 88 | input( 89 | idAttr := "fileinput", 90 | `type` := "file", 91 | inContext(thisNode => onChange --> { _ => todo(thisNode) }) 92 | ) 93 | 94 | // CHECKBOX 95 | def checkbox(isChecked: Boolean) = input(`type` := "checkbox", checked := isChecked) 96 | 97 | trait Displayable { 98 | def name: String 99 | } 100 | 101 | // BUTTONS 102 | 103 | def linkButton(content: String, link: String, buttonStyle: HESetters = bsn.btn_secondary, openInOtherTab: Boolean = true) = 104 | a(buttonStyle, href := link, role := "button", target := { 105 | if (openInOtherTab) "_blank" else "" 106 | }, content) 107 | 108 | // Clickable span containing a glyphicon and a text 109 | def glyphSpan(glyphicon: HESetters, modifier: Modifier[HtmlElement] = emptyMod, text: String = "") = 110 | span(text, glyphicon, cursor.pointer, aria.hidden := true, modifier) 111 | 112 | 113 | // Close buttons 114 | def closeButton(dataDismiss: String, todo: () => Unit = () => {}) = 115 | button( 116 | onClick --> { _ => todo() }, cls("close"), aria.label := "Close", dataAttr("dismiss") := dataDismiss, 117 | span(aria.hidden := true, "×") 118 | ) 119 | 120 | 121 | //TOGGLE BUTTON 122 | case class ToggleState[T](t: T, text: String, cls: String, todo: T => Unit = (_: T) => {}) 123 | 124 | case class ToggleButtonState[T](state: ToggleState[T], activeState: Boolean, unactiveState: ToggleState[T], onToggled: () => Unit, modifiers: HESetters, withCaret: Boolean) { 125 | 126 | val toggled = Var(activeState) 127 | 128 | lazy val element = button(`type` := "button", 129 | cls <-- toggled.signal.map(t => 130 | if (t) state.cls 131 | else unactiveState.cls 132 | ), 133 | child <-- toggled.signal.map { t => 134 | div(if (t) state.text else unactiveState.text, 135 | if (withCaret) span(bootstrapnative.bsn.glyph_right_caret) else emptyMod) 136 | }, 137 | onClick --> { _ => 138 | toggled.update(!_) 139 | onToggled() 140 | }, 141 | ).amend(modifiers: _*) 142 | 143 | } 144 | 145 | def toggle[T](activeState: ToggleState[T], default: Boolean, unactiveState: ToggleState[T], onToggled: () => Unit = () => {}, modifiers: HESetters = emptySetters, withCaret: Boolean = true) = { 146 | ToggleButtonState(activeState, default, unactiveState, onToggled, modifiers, withCaret) 147 | } 148 | 149 | 150 | case class RadioButtons[T](states: Seq[ToggleState[T]], activeStates: Seq[ToggleState[T]], unactiveStateClass: String, modifiers: HESetters = emptySetters) { 151 | 152 | lazy val active = Var(activeStates) 153 | 154 | lazy val element = div(bsnsheet.btnGroup, 155 | for (rb <- states) yield { 156 | val rbStateCls = active.signal.map(_.filter(_ == rb).headOption.map(_.cls).getOrElse(unactiveStateClass)) 157 | 158 | button( 159 | rb.text, 160 | cls <-- rbStateCls, 161 | onClick --> { _ => 162 | active.update { ac => 163 | val id = ac.indexOf(rb) 164 | if (id == -1) ac.appended(rb) 165 | else ac.patch(id, Nil, 1) 166 | } 167 | rb.todo(rb.t) 168 | } 169 | ) 170 | } 171 | ) 172 | } 173 | 174 | 175 | def radio[T](buttons: Seq[ToggleState[T]], activeStates: Seq[ToggleState[T]], unactiveStateClass: String, radioButtonsModifiers: HESetters = emptySetters) = 176 | RadioButtons(buttons, activeStates, unactiveStateClass, radioButtonsModifiers).element 177 | 178 | 179 | // RADIO 180 | enum SelectionSize: 181 | case DefaultLength, Infinite 182 | 183 | case class ExclusiveRadioButtons[T](buttons: Seq[ToggleState[T]], unactiveStateClass: String, defaultToggles: Seq[Int], selectionSize: SelectionSize = SelectionSize.DefaultLength, radioButtonsModifiers: HESetters) { 184 | 185 | val selected: Var[Seq[ToggleState[T]]] = Var(defaultToggles.map(buttons(_))) 186 | 187 | lazy val element = div(bsnsheet.btnGroup, bsnsheet.btnGroupToggle, dataAttr("toggle") := "buttons", 188 | buttons.zipWithIndex.map { case (rb, index) => 189 | child <-- selected.signal.map { as => 190 | val isInSelection = as.filter(_ == rb) //.map(_.cls) 191 | 192 | val bCls = isInSelection match { 193 | case Seq() => unactiveStateClass 194 | case _ => rb.cls 195 | } 196 | val isActive = rb == as.last 197 | 198 | label( 199 | cls := bCls, 200 | cls.toggle("focus active") := isActive, 201 | input(`type` := "radio"/*, name := "options"*/, idAttr := s"option${index + 1}", checked := isActive), 202 | rb.text, 203 | onClick --> { _ => 204 | selected.update(as => { 205 | val li = (as :+ rb).distinct 206 | selectionSize match { 207 | case SelectionSize.DefaultLength => if (li.size == defaultToggles.size) li else li.drop(1) 208 | case SelectionSize.Infinite => if (as.contains(rb)) as.filterNot(_ == rb) else li 209 | } 210 | } 211 | ) 212 | rb.todo(rb.t) 213 | } 214 | ) 215 | } 216 | } 217 | ) 218 | } 219 | 220 | def exclusiveRadio[T](buttons: Seq[ToggleState[T]], unactiveStateClass: String, defaultToggle: Int, radioButtonsModifiers: HESetters = emptySetters) = 221 | exclusiveRadios(buttons, unactiveStateClass, Seq(defaultToggle), SelectionSize.DefaultLength, radioButtonsModifiers) 222 | 223 | def exclusiveRadios[T](buttons: Seq[ToggleState[T]], unactiveStateClass: String, defaultToggles: Seq[Int], selectionSize: SelectionSize = SelectionSize.DefaultLength, radioButtonsModifiers: HESetters = emptySetters) = 224 | ExclusiveRadioButtons(buttons, unactiveStateClass, defaultToggles, selectionSize, radioButtonsModifiers) 225 | 226 | 227 | //Label decorators to set the label size 228 | implicit class TypedTagLabel(badge: Span) { 229 | def size1(modifierSeq: HESetters = emptySetters) = h1(modifierSeq, badge) 230 | 231 | def size2(modifierSeq: HESetters = emptySetters) = h2(modifierSeq, badge) 232 | 233 | def size3(modifierSeq: HESetters = emptySetters) = h3(modifierSeq, badge) 234 | 235 | def size4(modifierSeq: HESetters = emptySetters) = h4(modifierSeq, badge) 236 | 237 | def size5(modifierSeq: HESetters = emptySetters) = h5(modifierSeq, badge) 238 | 239 | def size6(modifierSeq: HESetters = emptySetters) = h6(modifierSeq, badge) 240 | } 241 | 242 | 243 | // PROGRESS BAR 244 | def progressBar(barMessage: String, ratio: Int) = 245 | div(bsnsheet.progress, 246 | div(bsnsheet.progressBar, width := ratio.toString() + "%", barMessage) 247 | ) 248 | 249 | 250 | // BADGE 251 | def badge(badgeValue: String, badgeStyle: HESetters) = span(cls("badge"), badgeStyle, marginLeft := "4", badgeValue) 252 | 253 | //BUTTON GROUP 254 | def buttonGroup = div(bsn.btnGroup) 255 | 256 | def buttonToolBar = div(bsn.btnToolbar, role := "toolbar") 257 | 258 | //MODAL 259 | case class ModalDialog(modalHeader: HtmlElement, 260 | modalBody: HtmlElement, 261 | modalFooter: HtmlElement, 262 | modifiers: HESetters = emptySetters, 263 | onopen: () => Unit = () => {}, 264 | onclose: () => Unit = () => {}) { 265 | 266 | lazy val render = { 267 | val d = div( 268 | bsnsheet.modal, bsn.fade, role := "dialog", aria.hidden := true, aria.labelledBy := uuID.short("m"), 269 | div(bsnsheet.modalDialog, modifiers, 270 | div(bsn.modalContent, 271 | div(bsnsheet.modalHeader, modalHeader), 272 | div(bsnsheet.modalBody, modalBody), 273 | div(bsnsheet.modalFooter, modalFooter) 274 | ) 275 | ) 276 | ) 277 | 278 | org.scalajs.dom.document.body.appendChild(d.ref) 279 | d 280 | } 281 | 282 | 283 | lazy val modal = new BSN.Modal(render.ref) 284 | 285 | def show = { 286 | modal.show() 287 | onopen() 288 | } 289 | 290 | def hide = { 291 | modal.hide() 292 | onclose() 293 | } 294 | 295 | def toggle = modal.toggle() 296 | 297 | } 298 | 299 | // def modalDialog(modalHeader: HtmlElement, modalBody: HtmlElement, modalFooter: HtmlElement, onopen: () => Unit, onclose: () => Unit, modifiers: HESetters = emptySetters) = 300 | // ModalDialog(modalHeader, modalBody, modalFooter, modifiers, onopen, onclose) 301 | 302 | 303 | // NAVS 304 | case class NavItem(contentDiv: HtmlElement, 305 | val todo: () ⇒ Unit = () ⇒ {}, 306 | extraRenderPair: HESetters = emptySetters, 307 | activeDefault: Boolean = false, 308 | toRight: Boolean = false) { 309 | 310 | val active: Var[Boolean] = Var(activeDefault) 311 | 312 | val render = li( 313 | cls := bsn.nav_item, 314 | 315 | a(href := "#", 316 | cls := bsn.nav_link, 317 | child <-- active.signal.map { _ => span(cls := "sr-only", "(current)") }, 318 | cls.toggle("active") <-- active.signal, 319 | lineHeight := "35px", 320 | onClick --> { _ => 321 | todo() 322 | }, 323 | contentDiv, 324 | cls.toggle("active") <-- active.signal, 325 | extraRenderPair 326 | ) 327 | ) 328 | 329 | def right = copy(toRight = true) 330 | } 331 | 332 | def navItem(content: HtmlElement, 333 | todo: () => Unit = () => {}, 334 | extraRenderPair: HESetters = emptySetters, 335 | activeDefault: Boolean = false, 336 | alignRight: Boolean = false) = 337 | NavItem(content: HtmlElement, todo, extraRenderPair, activeDefault, alignRight) 338 | 339 | 340 | def stringNavItem(content: String, todo: () ⇒ Unit = () ⇒ {}, activeDefault: Boolean = false): NavItem = 341 | navItem(span(content), todo, activeDefault = activeDefault) 342 | 343 | def navBar(classPair: HESetters, contents: NavItem*) = new NavBar(classPair, None, contents) 344 | 345 | case class NavBarBrand(src: String, modifierSeq: HESetters, todo: () => Unit, alt: String) 346 | 347 | case class NavBar(classPair: HESetters, brand: Option[NavBarBrand], contents: Seq[NavItem]) { 348 | 349 | val navId = uuID.short("n") 350 | 351 | def render: HtmlElement = { 352 | 353 | val sortedContents = contents.partition { 354 | _.toRight 355 | } 356 | 357 | def buildUL(cts: Seq[NavItem], modifier: HESetters = emptySetters) = 358 | ul(bsn.navbar_nav, modifier, 359 | cts.map { c ⇒ 360 | c.render.amend(onClick --> { _ ⇒ 361 | contents.foreach { 362 | _.active.set(false) 363 | } 364 | c.active.set(true) 365 | }) 366 | } 367 | ) 368 | 369 | val content = div(cls := "collapse navbar-collapse", idAttr := navId, 370 | buildUL(sortedContents._2, bsmargin.r.auto), 371 | buildUL(sortedContents._1, bsmargin.l.auto) 372 | ) 373 | 374 | navTag(bsn.navbar, bsn.navbar_expand_lg, classPair, 375 | for { 376 | b <- brand 377 | } yield { 378 | div( 379 | a(bsn.navbar_brand, href := "#", padding := "0", 380 | img(b.modifierSeq, cursor.pointer, alt := b.alt, src := b.src, onClick --> { 381 | _ => b.todo() 382 | }) 383 | ), 384 | button(cls := "navbar-toggler", 385 | `type` := "button", 386 | dataAttr("toggle") := "collapse", dataAttr("target") := s"#$navId", 387 | aria.controls := navId, aria.expanded := false, aria.label := "Toggle navigation", 388 | span(cls := "navbar-toggler-icon") 389 | ) 390 | ) 391 | }, content 392 | ) 393 | } 394 | 395 | def withBrand(src: String, modifierSeq: HESetters = emptySetters, todo: () => Unit = () => {}, alt: String = "") = copy(brand = Some(NavBarBrand(src, modifierSeq, todo, alt))) 396 | 397 | } 398 | 399 | //POPOVER 400 | type TypedContent = String | HtmlElement 401 | 402 | case class PopoverBuilder(element: HtmlElement, 403 | innerElement: HtmlElement, 404 | position: PopupPosition = Bottom, 405 | trigger: PopupType = HoverPopup, 406 | title: Option[String] = None, 407 | dismissible: Boolean = false) { 408 | lazy val render = element.amend( 409 | dataAttr("toggle") := "popover", 410 | dataAttr("html") := "true", 411 | dataAttr("content") := innerElement.ref.innerHTML, 412 | dataAttr("placement") := position.value, 413 | dataAttr("trigger") := { 414 | trigger match { 415 | case ClickPopup => "focus" 416 | case Manual => "manual" 417 | case _ => "hover" 418 | } 419 | }, 420 | title.map(dataAttr("title") := _).getOrElse(emptyMod), 421 | dataAttr("dismissible") := dismissible.toString, 422 | onClick --> (_ => popover.show()), 423 | ) 424 | 425 | lazy val popover: BSN.Popover = new BSN.Popover(render.ref /*, scalajs.js.Dynamic.literal("title" -> "euinesaurtie")*/) 426 | 427 | def show = popover.show() 428 | 429 | def hide = popover.hide() 430 | 431 | def toggle = popover.toggle() 432 | 433 | } 434 | 435 | //TOOLTIP 436 | class TooltipBuilder(element: HtmlElement, 437 | text: String, 438 | position: PopupPosition = Bottom, 439 | condition: () => Boolean = () => true) { 440 | 441 | val render: HtmlElement = { 442 | if (condition()) 443 | element.amend( 444 | dataAttr("placement") := position.value, 445 | dataAttr("toggle") := "tooltip", 446 | dataAttr("original-title") := text, 447 | onMouseOver --> { _ => tooltip.show } 448 | ) 449 | else element 450 | } 451 | 452 | val tooltip = new BSN.Tooltip(render.ref) 453 | 454 | def hide = tooltip.hide() 455 | } 456 | 457 | implicit class PopableTypedTag(element: HtmlElement) { 458 | 459 | def tooltip(text: String, 460 | position: PopupPosition = Bottom, 461 | condition: () => Boolean = () => true) = { 462 | new TooltipBuilder(element, text, position, condition).render 463 | } 464 | 465 | def popover(content: HtmlElement, 466 | position: PopupPosition = Bottom, 467 | trigger: PopupType = HoverPopup, 468 | title: Option[String] = None, 469 | dismissible: Boolean = false 470 | ) = 471 | PopoverBuilder(element, content, position, trigger, title, dismissible) 472 | } 473 | 474 | // //DROPDOWN 475 | implicit class SelectableSeqWithStyle[T](s: Seq[T]) { 476 | def options(defaultIndex: Int = 0, 477 | key: HESetters = emptySetters, 478 | naming: T => String, 479 | onclose: () => Unit = () => {}, 480 | onclickExtra: () ⇒ Unit = () ⇒ {}, 481 | decorations: Map[T, HESetters] = Map(), 482 | fixedTitle: Option[String] = None) = 483 | Selector.options(s, defaultIndex, key, naming, onclose, onclickExtra, decorations, fixedTitle) 484 | 485 | } 486 | 487 | // COLLAPSERS 488 | implicit class TagCollapserOnClick[S <: HtmlElement](trigger: S) { 489 | def expandOnclick[T <: HtmlElement](inner: T) = { 490 | 491 | val clicked: Var[Boolean] = Var(false) 492 | div( 493 | trigger.amend( 494 | onClick --> { _ => 495 | clicked.update(!_) 496 | } 497 | ), 498 | clicked.signal.expand(inner) 499 | ) 500 | } 501 | } 502 | 503 | implicit class TTagCollapserWithReactive(r: Signal[Boolean]) { 504 | 505 | def expand(inner: HtmlElement) = { 506 | div(overflow.hidden, 507 | transition := "height 300ms", 508 | height <-- r.map { rr => 509 | if (rr) inner.ref.style.height 510 | else "0px" 511 | }, 512 | inner 513 | ) 514 | } 515 | } 516 | 517 | 518 | // TABS 519 | type TabID = String 520 | 521 | case class Tab[T](t: T, 522 | title: HtmlElement, 523 | content: HtmlElement, 524 | active: Boolean = false, 525 | onClicked: () => Unit = () => {}, 526 | onAdded: () => Unit = () => {}, 527 | onRemoved: () => Unit = () => {}, 528 | tabID: TabID = uuID.short("t"), 529 | refID: String = uuID.short("r")) 530 | 531 | object Tabs { 532 | def tabs[T](initialTabs: Seq[Tab[T]] = Seq(), isClosable: Boolean = false, tabStyle: HESetters = bsnsheet.navTabs) = TabHolder(initialTabs, isClosable, 0, (tab: Tab[T]) => {}, tabStyle) 533 | 534 | 535 | // def defaultSortOptions: (Var[Seq[Tab]], Int => Unit) => SortableOptions = (ts: Var[Seq[Tab]], setActive: Int => Unit) => 536 | // SortableOptions.onEnd( 537 | // (event: EventS) ⇒ { 538 | // val oldI = event.oldIndex.asInstanceOf[Int] 539 | // val newI = event.newIndex.asInstanceOf[Int] 540 | // ts.update(cur => cur.updated(oldI, cur(newI)).updated(newI, cur(oldI))) 541 | // // ts() = ts.now.updated(oldI, ts.now(newI)).updated(newI, ts.now(oldI)) 542 | // setActive(newI) 543 | // } 544 | // ) 545 | 546 | case class TabHolder[T](tabs: Seq[Tab[T]], isClosable: Boolean, initIndex: Int /*, sortableOptions: Option[(Var[Seq[Tab]], Int => Unit) => SortableOptions]*/ , onActivation: Tab[T] => Unit, tabStyle: HESetters) { 547 | def add(tab: Tab[T]): TabHolder[T] = { 548 | val ts = copy(tabs = this.tabs :+ tab) 549 | tab.onAdded() 550 | ts 551 | } 552 | 553 | def closable = copy(isClosable = true) 554 | 555 | def initialIndex(index: Int) = copy(initIndex = index) 556 | 557 | // def withSortableOptions(options: (Var[Seq[Tab]], Int => Unit) => SortableOptions) = copy(sortableOptions = Some(options)) 558 | 559 | // def onActivation(onActivation: Tab[T] => Unit = Tab => {}) = copy(onActivation = onActivation) 560 | 561 | def build = { 562 | Tabs(tabs, isClosable, tabStyle = tabStyle) 563 | } 564 | } 565 | 566 | } 567 | 568 | case class Tabs[T](initialTabs: Seq[Tab[T]], isClosable: Boolean, tabStyle: HESetters) { 569 | 570 | val tabs = Var(initialTabs) 571 | 572 | if (!initialTabs.map(_.active).exists(_ == true)) 573 | setFirstActive 574 | 575 | //def activeTab = tabs.now().filter(_.active).headOption 576 | val activeTab = tabs.signal.map { t => 577 | t.filter { 578 | _.active 579 | } 580 | } 581 | 582 | 583 | def setFirstActive = tabs.now().headOption.foreach { t => 584 | setActive(t.tabID) 585 | } 586 | 587 | def setActive(tabID: TabID) = { 588 | tabs.update { ts => 589 | ts.map { t => t.copy(active = t.tabID == tabID) } 590 | } 591 | } 592 | 593 | def tab(tabID: TabID) = tabs.now().filter { 594 | _.tabID == tabID 595 | }.headOption 596 | 597 | def add(tab: Tab[T], activate: Boolean = true) = { 598 | tabs.update(t => t :+ tab) 599 | if (activate) setActive(tab.tabID) 600 | } 601 | 602 | def isActive(id: TabID) = tab(id).map { 603 | _.active 604 | }.getOrElse(false) 605 | 606 | def remove(tabID: TabID) = { 607 | tab(tabID).map { 608 | _.onRemoved() 609 | } 610 | 611 | //Remove tab 612 | tabs.update(t => t.filterNot(_.tabID == tabID)) 613 | 614 | //Fix active tab 615 | if (tabs.now().length > 0 && tabs.now().map { 616 | _.active 617 | }.forall(_ == false)) { 618 | tabs.update { e => 619 | e.head.copy(active = true) +: e.tail 620 | } 621 | } 622 | } 623 | 624 | //def onclose(f: (Tab) => Unit) = copy(onCloseExtra = f) 625 | 626 | lazy val tabClose: HESetters = Seq( 627 | position := "relative", 628 | fontSize := "20", 629 | color := "black", 630 | right := "-10", 631 | opacity := "0.3", 632 | width := "20" 633 | ) 634 | 635 | case class TabRender(tabHeader: Li, tabContent: Div) 636 | 637 | def renderTab(tabID: TabID, initialTab: Tab[T], tabStream: Signal[Tab[T]]): TabRender = { 638 | 639 | 640 | val header = li( 641 | cls := bsn.nav_item, 642 | a( 643 | cls := bsn.nav_link, 644 | cls.toggle("active") <-- tabStream.map { t => 645 | t.active 646 | }, 647 | a(idAttr := tabID, 648 | bsn.tab_role, 649 | cursor.pointer, 650 | dataAttr("toggle") := "tab", 651 | dataAttr("height") := "true", 652 | aria.controls <-- tabStream.map { t => t.refID }, 653 | onClick --> { _ => 654 | setActive(tabID) 655 | initialTab.onClicked() 656 | }, 657 | if (isClosable) button(cls := "close", tabClose, `type` := "button", "×", 658 | onClick --> { e => 659 | remove(initialTab.tabID) 660 | e.stopPropagation() 661 | }) else span(), 662 | child <-- tabStream.map { 663 | _.title 664 | } 665 | ) 666 | ) 667 | ) 668 | 669 | val tabDiv = 670 | // div(idAttr <-- tabStream.map { t => t.refID }, 671 | div(bsn.tab_content, 672 | div(bsn.tab_panel_role, 673 | bsn.tab_pane, bsn.fade, 674 | cls.toggle("active show") <-- tabStream.map(_.active), 675 | child <-- tabStream.map { t => t.content } 676 | ) 677 | ) 678 | 679 | //FIXME 680 | // Sortable(header, sortableOptions(tabs, setActive)) 681 | 682 | TabRender(header, tabDiv) 683 | } 684 | 685 | def render: Div = render("") 686 | 687 | def render(contentClass: String): Div = { 688 | div(children <-- tabs.signal.split(_.tabID)(renderTab).map { tr => 689 | Seq( 690 | ul(bsn.nav, tabStyle, tr.map(_.tabHeader)), 691 | div(bsn.tab_content, paddingTop := "10", tr.map(_.tabContent), cls := contentClass) 692 | ) 693 | }) 694 | } 695 | } 696 | 697 | // 698 | // //TABLE 699 | 700 | def elementTable(rows: Seq[Row] = Seq()) = ElementTableBuilder(rows) 701 | 702 | def dataTable(rows: Seq[Seq[DataContent]] = Seq()) = DataTableBuilder(rows) 703 | 704 | // // FORMS 705 | trait FormTag { 706 | def tag: HtmlElement 707 | } 708 | 709 | trait LabeledFormTag extends FormTag { 710 | def label: Label 711 | } 712 | 713 | implicit def htmlElementToFormTag(t: HtmlElement): FormTag = new FormTag { 714 | val tag: HtmlElement = t 715 | } 716 | 717 | implicit class LabelForModifiers(m: HtmlElement) { 718 | def withLabel(title: String, labelStyle: HESetters = emptySetters): LabeledFormTag = new LabeledFormTag { 719 | val label: Label = com.raquo.laminar.api.L.label(title, labelStyle, paddingRight := "5") 720 | 721 | val tag: HtmlElement = m 722 | } 723 | } 724 | 725 | private def insideForm(elements: Seq[FormTag]) = 726 | for { 727 | ft <- elements 728 | } yield { 729 | div(bsnsheet.formGroup, paddingRight := "5", 730 | ft match { 731 | case lft: LabeledFormTag => lft.label 732 | case _ => span() 733 | }, 734 | ft.tag 735 | ) 736 | } 737 | 738 | 739 | def vForm(elements: FormTag*): HtmlElement = vForm(emptySetters, elements: _*) 740 | 741 | 742 | def vForm(heSetters: HESetters, elements: FormTag*): HtmlElement = 743 | div(heSetters :+ bsnsheet.formVertical, insideForm(elements)) 744 | 745 | 746 | def hForm(formTags: FormTag*): HtmlElement = hForm(emptySetters, formTags: _*) 747 | 748 | def hForm(heSetters: HESetters, formTags: FormTag*): HtmlElement = { 749 | form(bsn.formInline, heSetters, insideForm(formTags)) 750 | } 751 | 752 | //TOAST 753 | type ToastPosition = HESetters 754 | type ToastID = String 755 | 756 | case class ToastHeader(text: String, comment: String = "", backgroundColor: String = "#fff") 757 | 758 | case class Toast(header: ToastHeader, bodyText: String, toastPosition: ToastPosition = bsn.bottomRightPosition, delay: Option[Int] = None, toastID: ToastID = uuID.short("t")) 759 | 760 | def toast(toastHeader: ToastHeader, bodyText: String, toastPosition: ToastPosition = bsn.bottomRightPosition, delay: Option[Int] = None) = 761 | Toast(toastHeader, bodyText, toastPosition, delay) 762 | 763 | case class ToastStack(toastPosition: ToastPosition, unstackOnClose: Boolean, initialToasts: Seq[Toast]) { 764 | 765 | val toasts = Var(initialToasts) 766 | val activeToasts = Var(Seq[Toast]()) 767 | 768 | def toast(toastID: ToastID) = toasts.now().filter(_.toastID == toastID) 769 | 770 | def stack(toast: Toast) = 771 | if (!toasts.now().exists(_ == toast)) 772 | toasts.update(ts => ts :+ toast) 773 | 774 | def unstack(toast: Toast) = toasts.update(ts => ts.filterNot(_ == toast)) 775 | 776 | def show(toast: Toast) = 777 | if (!activeToasts.now().exists(_ == toast)) { 778 | activeToasts.update(ts => ts :+ toast) 779 | toast.delay.foreach { d => 780 | scalajs.js.timers.setTimeout(d)(hide(toast)) 781 | } 782 | } 783 | 784 | def stackAndShow(toast: Toast) = { 785 | stack(toast) 786 | show(toast) 787 | } 788 | 789 | def hide(toast: Toast) = activeToasts.update(at => at.filterNot(_ == toast)) 790 | 791 | 792 | def toastRender(toastID: ToastID, initialToast: Toast, toastStream: Signal[Toast]): Div = { 793 | 794 | val isActive = activeToasts.signal.map { ts => ts.contains(initialToast) } 795 | 796 | div( 797 | bsnsheet.toastCls, role := "alert", aria.live := "assertive", aria.atomic := true, dataAttr("animation") := "true", 798 | cls <-- isActive.map { a => 799 | if (a) "fade show" else "fade hide" 800 | }, 801 | div(bsn.toastHeader, backgroundColor := initialToast.header.backgroundColor, 802 | strong(bsmargin.r.auto, initialToast.header.text), 803 | small(initialToast.header.comment), 804 | button(`type` := "button", bsmargin.l.two, bsmargin.b.one, cls := "close", dataAttr("dismiss") := "toast", aria.label := "Close", 805 | span(aria.hidden := true, "×"), 806 | onClick --> { e => 807 | hide(initialToast) 808 | if (unstackOnClose) 809 | unstack(initialToast) 810 | } 811 | ) 812 | ), 813 | div(bsn.toastBody, initialToast.bodyText) 814 | ) 815 | } 816 | 817 | val render = { 818 | div( 819 | toastPosition, 820 | children <-- toasts.signal.split(_.toastID)(toastRender) 821 | ) 822 | } 823 | } 824 | 825 | def toastStack(toastPosition: ToastPosition, unstackOnClose: Boolean, toasts: Toast*) = ToastStack(toastPosition: ToastPosition, unstackOnClose, toasts) 826 | } 827 | 828 | 829 | 830 | 831 | -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/JsDependency.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | import com.raquo.laminar.api.L.{*, given} 3 | 4 | trait JSDependency{ 5 | def path: String 6 | } 7 | 8 | object JSDependency { 9 | 10 | lazy val BOOTSTRAP_NATIVE = new JSDependency{ def path = "js/bootstrap-native.min.js" } 11 | 12 | 13 | def withBootstrapNative[T <: HtmlElement](f: => T): Unit = withJS(BOOTSTRAP_NATIVE)(f) 14 | 15 | def withJS[T <: HtmlElement](js: JSDependency*)(f: => T): Unit = { 16 | org.scalajs.dom.document.body.appendChild(f.ref) 17 | for { 18 | j <- js 19 | } yield { 20 | org.scalajs.dom.document.body.appendChild( 21 | scriptTag(`type` := "text/javascript", src := j.path).ref 22 | ) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/Popup.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | 4 | /* 5 | * Copyright (C) 02/05/16 // mathieu.leclaire@openmole.org 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | object Popup { 22 | 23 | sealed trait PopupPosition { 24 | def value: String 25 | } 26 | 27 | object Left extends PopupPosition { 28 | def value = "left" 29 | } 30 | 31 | object Right extends PopupPosition { 32 | def value = "right" 33 | } 34 | 35 | object Top extends PopupPosition { 36 | def value = "top" 37 | } 38 | 39 | object Bottom extends PopupPosition { 40 | def value = "bottom" 41 | } 42 | 43 | sealed trait PopupType 44 | 45 | object HoverPopup extends PopupType 46 | 47 | object ClickPopup extends PopupType 48 | 49 | object Manual extends PopupType 50 | 51 | object DialogPopup extends PopupType 52 | 53 | } 54 | -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/Selector.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | /* 4 | * Copyright (C) 13/01/15 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import scaladget.bootstrapnative.bsn._ 21 | import scaladget.tools.Utils._ 22 | import com.raquo.laminar.api.L.{*, given} 23 | import org.scalajs.dom.raw.Event 24 | 25 | object Selector { 26 | 27 | def options[T](contents: Seq[T], 28 | defaultIndex: Int = 0, 29 | key: HESetters = emptySetters, 30 | naming: T => String, 31 | onclose: () => Unit = () => {}, 32 | onclickExtra: () => Unit = () => {}, 33 | decorations: Map[T, HESetters] = Map(), 34 | fixedTitle: Option[String] = None) = new Options(contents, defaultIndex, key, naming, onclose, onclickExtra, decorations, fixedTitle) 35 | 36 | 37 | class Options[T](private val _contents: Seq[T], 38 | defaultIndex: Int = 0, 39 | key: HESetters = emptySetters, 40 | naming: T => String, 41 | onclose: () => Unit, 42 | onclickExtra: () => Unit = () => {}, 43 | decorations: Map[T, HESetters], 44 | fixedTitle: Option[String]) { 45 | 46 | implicit def tToString(t: T): String = naming(t) 47 | 48 | val contents = Var(_contents) 49 | val opened = Var(false) 50 | val autoID = uuID.short("dd") 51 | 52 | val content: Var[Option[T]] = Var(_contents.size match { 53 | case 0 ⇒ None 54 | case _ ⇒ 55 | if (defaultIndex < _contents.size) Some(_contents(defaultIndex)) 56 | else _contents.headOption 57 | }) 58 | 59 | def setContents(cts: Seq[T], onset: () ⇒ Unit = () ⇒ {}) = { 60 | contents.set(cts) 61 | content.set(cts.headOption) 62 | onset() 63 | } 64 | 65 | def emptyContents = { 66 | contents.set(Seq()) 67 | content.set(None) 68 | } 69 | 70 | def set(t: T) = content.set(Some(t)) 71 | 72 | def get: Option[T] = content.now() 73 | 74 | def getOrElse(t: T) = get.getOrElse(t) 75 | 76 | def isContentsEmpty = contents.now().isEmpty 77 | 78 | def close = opened.set(false) 79 | 80 | lazy val selector = 81 | div( 82 | cls := "dropdown", 83 | cls.toggle("show") <-- opened.signal, 84 | button(idAttr := autoID, 85 | `type` := "button", 86 | dataAttr("toggle") := "dropdown", 87 | key, 88 | aria.hasPopup := true, 89 | aria.expanded <-- opened.signal, 90 | child.text <-- content.signal.map { c => fixedTitle.getOrElse(c.map(naming).getOrElse("")) }, 91 | span(caret), 92 | onClick --> { _ => 93 | opened.set(!opened.now()) 94 | onclickExtra() 95 | } 96 | ), 97 | div( 98 | dropdownMenu, 99 | cls.toggle("show") <-- opened.signal, 100 | aria.labelledBy(autoID), 101 | for { 102 | c <- contents.now() 103 | } yield { 104 | a( 105 | cls := "dropdown-item", 106 | href := "#", 107 | decorations.getOrElse(c, emptySetters).toSeq, 108 | s" ${naming(c)}", 109 | onClick --> { _ => 110 | content.set(Some(c)) 111 | opened.update(!_) 112 | onclose() 113 | } 114 | ) 115 | } 116 | ) 117 | ) 118 | 119 | selector.ref.addEventListener("mousedown", (e: Event) => { 120 | e.stopPropagation() 121 | }) 122 | 123 | selector.ref.onClickOutside(() => { 124 | close 125 | 126 | }) 127 | } 128 | 129 | 130 | } 131 | 132 | -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/Table.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | import scaladget.bootstrapnative.bsn.* 4 | import com.raquo.laminar.api.L.{*, given} 5 | import scaladget.bootstrapnative.Table.{Column, DataContent} 6 | import scaladget.tools.Utils.* 7 | 8 | import scala.util.Try 9 | 10 | object Sorting { 11 | trait Sorting 12 | 13 | object NoSorting extends Sorting 14 | 15 | object PhantomSorting extends Sorting 16 | 17 | object AscSorting extends Sorting 18 | 19 | object DescSorting extends Sorting 20 | 21 | case class SortingStatus(col: Int, sorting: Sorting) 22 | 23 | val defaultSortingStatus = SortingStatus(0, AscSorting) 24 | 25 | def sortInt(seq: Seq[String]) = Try( 26 | seq.map(_.toInt).zipWithIndex.sortBy(_._1).map(_._2) 27 | ).toOption 28 | 29 | def sortDouble(seq: Seq[String]) = Try( 30 | seq.map(_.toDouble).zipWithIndex.sortBy(_._1).map(_._2) 31 | ).toOption 32 | 33 | def sortString(seq: Seq[String]): Seq[Int] = seq.zipWithIndex.sortBy(_._1).map(_._2) 34 | 35 | def sort(s: Column): Seq[Int] = { 36 | sortInt(s.values.map(DataContent.stringContent)) match { 37 | case Some(i: Seq[_]) => i 38 | case None => sortDouble(s.values.map(DataContent.stringContent)) match { 39 | case Some(d: Seq[_]) => d 40 | case None => sortString(s.values.map(DataContent.stringContent)) 41 | } 42 | } 43 | } 44 | } 45 | 46 | import Sorting._ 47 | 48 | object Table { 49 | 50 | type RowID = String 51 | val SELECTION_COLOR = "#52adf233" 52 | 53 | val centerCell = Seq(verticalAlign.middle, textAlign.center) 54 | 55 | case class BSTableStyle(tableStyle: HESetters = emptySetters, 56 | headerStyle: HESetters = emptySetters, 57 | rowStyle: HESetters = emptySetters, 58 | selectionColor: String = SELECTION_COLOR) 59 | 60 | case class Header(values: Seq[String]) 61 | 62 | trait Row { 63 | val rowID: RowID = uuID.short("row") 64 | 65 | def tds: Seq[HtmlElement] 66 | } 67 | 68 | case class BasicRow(elements: Seq[HtmlElement]) extends Row { 69 | def tds = elements.map { 70 | td(_, centerCell) 71 | } 72 | } 73 | 74 | case class ExpandedRow(element: HtmlElement, signal: Signal[Boolean]) extends Row { 75 | 76 | def tds: Seq[HtmlElement] = 77 | Seq( 78 | td( 79 | colSpan := 999, padding := "0", borderTop := "0px solid black", 80 | signal.expand(element) 81 | ) 82 | ) 83 | } 84 | 85 | 86 | object DataContent: 87 | given Conversion[String, DataContent] = s => DataContent(s, None) 88 | 89 | def toHtml(c: DataContent) = c.html.getOrElse(span(c.value)) 90 | def stringContent(c: DataContent) = c.value 91 | 92 | case class DataContent(value: String, html: Option[HtmlElement] = None) 93 | 94 | case class Column(values: Seq[DataContent]) 95 | 96 | case class DataRow(values: Seq[DataContent]) extends Row { 97 | 98 | def tds: Seq[HtmlElement] = values.map { v => 99 | td(DataContent.toHtml(v), centerCell) 100 | } 101 | } 102 | 103 | def column(index: Int, rows: Seq[DataRow]): Column = Column(rows.map { 104 | _.values(index) 105 | }) 106 | 107 | def headerRender(headers: Option[Table.Header] = None, 108 | headerStyle: HESetters = emptySetters, 109 | sortingDiv: Option[Int => Span] = None) = 110 | thead(headerStyle, 111 | tr( 112 | headers.map { 113 | h => 114 | h.values.zipWithIndex.map { case (v, id) => 115 | th(centerCell, v, sortingDiv.map { f => f(id) }.getOrElse(emptyNode)) 116 | } 117 | })) 118 | 119 | 120 | case class ElementTable(initialRows: Seq[Row], 121 | headers: Option[Table.Header] = None, 122 | bsTableStyle: BSTableStyle = Table.BSTableStyle(default_table), 123 | showSelection: Boolean) { 124 | 125 | 126 | val rows = Var(initialRows) 127 | val selected: Var[Option[RowID]] = Var(None) 128 | val expanded: Var[Seq[RowID]] = Var(Seq()) 129 | 130 | 131 | def updateExpanded(rowID: RowID) = expanded.update { e => 132 | if (e.contains(rowID)) e.filterNot(_ == rowID) 133 | else e.appended(rowID) 134 | } 135 | 136 | def rowRender(rowID: RowID, initialRow: Row, rowStream: Signal[Row]): HtmlElement = { 137 | 138 | initialRow match { 139 | case br: BasicRow => 140 | tr(bsTableStyle.rowStyle, 141 | backgroundColor <-- selected.signal.map { 142 | s => if (Some(initialRow.rowID) == s) bsTableStyle.selectionColor else "" 143 | }, 144 | onClick --> (_ => if (showSelection) selected.set(Some(initialRow.rowID))), 145 | children <-- rowStream.map(r => r.tds) 146 | ) 147 | case er: ExpandedRow => 148 | tr( 149 | child <-- rowStream.map(rs => rs.tds.head) 150 | ) 151 | } 152 | } 153 | 154 | val render = 155 | table(bsTableStyle.tableStyle, 156 | headerRender(headers, bsTableStyle.headerStyle), 157 | tbody( 158 | children <-- rows.signal.split(_.rowID)(rowRender) 159 | ) 160 | ) 161 | 162 | } 163 | 164 | 165 | case class DataTable(initialRows: Seq[DataRow], 166 | headers: Option[Table.Header] = None, 167 | bsTableStyle: BSTableStyle = Table.BSTableStyle(default_table), 168 | sorting: Boolean = false) { 169 | 170 | type Filter = String 171 | val rows = Var(initialRows) 172 | val filterString: Var[Filter] = Var("") 173 | val selected: Var[Option[RowID]] = Var(None) 174 | 175 | 176 | //SORTING 177 | val nbColumns = initialRows.headOption.map(_.values.length).getOrElse(0) 178 | 179 | val sortingStatus: Var[SortingStatus] = Var(defaultSortingStatus) 180 | 181 | def columnSort(filteredRows: Seq[DataRow], sortingStatus: SortingStatus) = { 182 | val col = column(sortingStatus.col, filteredRows) 183 | val indexes: Seq[Int] = { 184 | val sorted = Sorting.sort(col) 185 | sortingStatus.sorting match { 186 | case DescSorting => sorted.reverse 187 | case _ => sorted 188 | } 189 | } 190 | 191 | for ( 192 | i <- indexes 193 | ) yield filteredRows(i) 194 | } 195 | 196 | def sortingGlyph(sortingStatus: SortingStatus) = 197 | sortingStatus.sorting match { 198 | case PhantomSorting => glyph_sort_down_alt 199 | case AscSorting => glyph_sort_down_alt 200 | case DescSorting => glyph_sort_down 201 | case _ => "" 202 | } 203 | 204 | def sortingGlyphOpacity(n: Int, selectedColumun: Int): Double = { 205 | if (n == selectedColumun) 1 206 | else 0.4 207 | } 208 | 209 | val sortingDiv = (n: Int) => 210 | // val ss = sortingStatuses() 211 | span(cursor.pointer, float.right, fontSize := "23", 212 | opacity <-- sortingStatus.signal.map { ss => sortingGlyphOpacity(n, ss.col) }, 213 | cls <-- sortingStatus.signal.map(ss => sortingGlyph(ss)), 214 | onClick --> { _ => 215 | if (sorting) { 216 | sortingStatus.update(ss => 217 | SortingStatus(n, 218 | ss.sorting match { 219 | case DescSorting | PhantomSorting => AscSorting 220 | case AscSorting => DescSorting 221 | case _ => PhantomSorting 222 | }) 223 | ) 224 | } 225 | } 226 | ) 227 | 228 | //FILTERING 229 | def rowFilter = (r: DataRow, filter: Filter) => r.values.mkString("|").toUpperCase.contains(filter) 230 | 231 | def setFilter(s: Filter) = filterString.set(s.toUpperCase) 232 | 233 | 234 | //RENDERING 235 | def rowRender(rowID: RowID, initialRow: Row, rowStream: Signal[Row]): HtmlElement = 236 | tr(bsTableStyle.rowStyle, 237 | backgroundColor <-- selected.signal.map { 238 | s => 239 | if (Some(initialRow.rowID) == s) bsTableStyle.selectionColor else "" 240 | }, 241 | onClick --> (_ => 242 | selected.set(Some(initialRow.rowID))), 243 | children <-- rowStream.map(r => r.tds) 244 | ) 245 | 246 | def render = table(bsTableStyle.tableStyle, 247 | headerRender(headers, bsTableStyle.headerStyle, Some(sortingDiv)), 248 | tbody(overflow.auto, 249 | children <-- rows.signal.combineWith(filterString.signal).combineWith(sortingStatus.signal).map { 250 | case (dr, f, s) => 251 | val filteredRows = dr.filter { d => rowFilter(d, f) } 252 | columnSort(filteredRows, s) 253 | }.split(_.rowID)(rowRender) 254 | ) 255 | ) 256 | 257 | } 258 | 259 | 260 | case class DataTableBuilder(initialRows: Seq[Seq[DataContent]], 261 | headers: Option[Table.Header] = None, 262 | bsTableStyle: BSTableStyle = Table.BSTableStyle(bordered_table), 263 | sorting: Boolean = false) { 264 | 265 | def addHeaders(hs: String*) = copy(headers = Some(Header(hs))) 266 | 267 | def addRow(values: DataContent*) = copy(initialRows = initialRows :+ values) 268 | 269 | def style(tableStyle: HESetter = default_table, headerStyle: HESetters = emptySetters, rowStyle: HESetters = emptySetters, selectionColor: String = SELECTION_COLOR) = { 270 | copy(bsTableStyle = BSTableStyle(tableStyle, headerStyle, rowStyle, selectionColor)) 271 | } 272 | 273 | def sortable = copy(sorting = true) 274 | 275 | lazy val render = DataTable(initialRows.map(DataRow(_)), headers, bsTableStyle, sorting) 276 | } 277 | 278 | 279 | case class ElementTableBuilder(initialRows: Seq[Row], 280 | headers: Option[Table.Header] = None, 281 | bsTableStyle: BSTableStyle = Table.BSTableStyle(bordered_table), 282 | showSelection: Boolean = true) { 283 | 284 | def addHeaders(hs: String*) = copy(headers = Some(Header(hs))) 285 | 286 | def addRow(row: BasicRow): ElementTableBuilder = copy(initialRows = initialRows :+ row) 287 | 288 | def addRow(values: HtmlElement*): ElementTableBuilder = addRow(BasicRow(values)) 289 | 290 | def style(tableStyle: HESetter = default_table, headerStyle: HESetters = emptySetters, rowStyle: HESetters = emptySetters, selectionColor: String = "#e1e1e1") = { 291 | copy(bsTableStyle = BSTableStyle(tableStyle, headerStyle, rowStyle, selectionColor)) 292 | } 293 | 294 | def unshowSelection = copy(showSelection = false) 295 | 296 | def expandTo(element: HtmlElement, signal: Signal[Boolean]) = { 297 | val lastRow = initialRows.last 298 | 299 | lastRow match { 300 | case br: BasicRow => copy(initialRows = initialRows.appended(ExpandedRow(element, signal))) 301 | case _ => this 302 | } 303 | } 304 | 305 | lazy val render = ElementTable(initialRows, headers, bsTableStyle, showSelection) 306 | } 307 | 308 | } -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/Tools.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | import com.raquo.laminar.api.L.{*, given} 4 | import com.raquo.laminar.nodes.ReactiveElement 5 | import scaladget.bootstrapnative.bsn._ 6 | import scaladget.bootstrapnative.Popup.{Bottom, HoverPopup, PopupPosition, PopupType} 7 | 8 | object Tools { 9 | 10 | 11 | case class MyPopoverBuilder(element: ReactiveElement[org.scalajs.dom.Element], 12 | popoverElement: HtmlElement, 13 | position: PopupPosition = Popup.Right, 14 | trigger: PopupType = HoverPopup) { 15 | 16 | private val open = Var(false) 17 | 18 | private def openTrigger = open.update(!_) 19 | 20 | def render = { 21 | element.amend( 22 | onClick --> { _=> openTrigger}, 23 | open.signal.expand( 24 | popoverElement.amend( 25 | onMouseLeave --> { _ => open.set(false) }, 26 | ) 27 | )) 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/bootstrapnative.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | /* 4 | * Copyright (C) 18/08/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | import com.raquo.laminar.api.L.{*, given} 22 | 23 | import scala.scalajs.js 24 | import scala.scalajs.js.annotation._ 25 | import scala.scalajs.js.Dynamic.{literal => lit} 26 | 27 | package object bsnsheet extends stylesheet.BootstrapPackage with stylesheet2.Bootstrap2Package 28 | 29 | package object bsn extends stylesheet.BootstrapPackage with stylesheet2.Bootstrap2Package with BootstrapTags 30 | 31 | //@js.native 32 | //@JSImport("bootstrap.native", JSImport.Namespace) 33 | //class Modal(element: Element) extends js.Object { 34 | // 35 | // def show(): Unit = js.native 36 | // 37 | // def hide(): Unit = js.native 38 | //} 39 | //@js.native 40 | //@JSImport("bootstrap.native/lib/V3/utils.js", JSImport.Namespace) 41 | //object Utils extends js.Object 42 | 43 | //@js.native 44 | //@JSImport("bootstrap.native/src/components/popover-native.js", JSImport.Namespace) 45 | //object Popover extends js.Object { 46 | // def apply(element: org.scalajs.dom.Element, options: js.Dynamic = lit()): Popover = js.native 47 | //} 48 | 49 | 50 | object BSN { 51 | 52 | @js.native 53 | @JSImport("bootstrap.native/src/components/popover-native.js", JSImport.Default) 54 | class Popover(element: org.scalajs.dom.Element, options: js.Dynamic = lit()) extends js.Object { 55 | def show(): Unit = js.native 56 | 57 | def hide(): Unit = js.native 58 | 59 | def toggle(): Unit = js.native 60 | } 61 | 62 | @js.native 63 | @JSImport("bootstrap.native/src/components/modal-native.js", JSImport.Default) 64 | class Modal(element: org.scalajs.dom.Element, options: js.Dynamic = lit()) extends js.Object { 65 | def show(): Unit = js.native 66 | 67 | def hide(): Unit = js.native 68 | 69 | def toggle(): Unit = js.native 70 | } 71 | 72 | 73 | @js.native 74 | @JSImport("bootstrap.native/src/components/tooltip-native.js", JSImport.Default) 75 | class Tooltip(element: org.scalajs.dom.Element, options: js.Dynamic = lit()) extends js.Object { 76 | 77 | def show: Unit = js.native 78 | 79 | def hide(): Unit = js.native 80 | 81 | def toggle(): Unit = js.native 82 | 83 | def close(): Unit = js.native 84 | } 85 | 86 | } 87 | 88 | //@JSGlobal 89 | //@js.native 90 | //class Collapse(element: Element, options: js.Dynamic = lit()) extends js.Object { 91 | // 92 | // def show(): Unit = js.native 93 | // 94 | // def hide(): Unit = js.native 95 | // 96 | // def toggle(): Unit = js.native 97 | //} -------------------------------------------------------------------------------- /bootstrapnative/src/main/scala/scaladget/bootstrapnative/stylesheet.scala: -------------------------------------------------------------------------------- 1 | package scaladget.bootstrapnative 2 | 3 | /* 4 | * Copyright (C) 30/03/16 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | import com.raquo.laminar.api.L.{*, given} 22 | 23 | package stylesheet { 24 | 25 | //import com.raquo.laminar.defs.ReactiveComplexHtmlKeys 26 | import com.raquo.laminar.nodes 27 | import com.raquo.laminar.nodes.{ReactiveElement, ReactiveHtmlElement} 28 | 29 | //package object bootstrap extends BootstrapPackage 30 | 31 | trait BootstrapPackage { 32 | 33 | 34 | private def toGlyphicon(s: String) = cls(s"$s") 35 | 36 | private def toBadge(s: String) = cls(s"badge $s") 37 | 38 | private def toButton(s: String) = cls(s"btn $s") 39 | 40 | private def toAlert(s: String) = cls(s"alert $s") 41 | 42 | private def toNav(s: String) = cls(s"nav $s") 43 | 44 | private def toTable(s: String) = cls(s"table $s") 45 | 46 | // implicit class ExtendBSButton(bsButton: BSButton) { 47 | // def outlined = bsButton. .key.name.replaceFirst("btn-","btn-outline") 48 | // } 49 | 50 | object spacing { 51 | type BSSpacing = String 52 | 53 | implicit class ABSMargin(bsSpacing: BSSpacing) { 54 | private def pattern(suffix: String) = cls := s"$bsSpacing-$suffix" 55 | 56 | def zero = pattern("0") 57 | def one = pattern("1") 58 | def two = pattern("2") 59 | def three = pattern("3") 60 | def four = pattern("4") 61 | def five = pattern("5") 62 | 63 | def auto = pattern("auto") 64 | } 65 | 66 | //SPACING 67 | object bsmargin { 68 | // right, left, top, bottom, left and right (x), top and bottom (y) 69 | def r: BSSpacing = "mr" 70 | 71 | def l: BSSpacing = "ml" 72 | 73 | def t: BSSpacing = "mt" 74 | 75 | def b: BSSpacing = "mb" 76 | 77 | def x: BSSpacing = "mx" 78 | 79 | def y: BSSpacing = "my" 80 | } 81 | 82 | object bspadding { 83 | // right, left, top, bottom, left and right (x), top and bottom (y) 84 | def r: BSSpacing = "pr" 85 | 86 | def l: BSSpacing = "pl" 87 | 88 | def t: BSSpacing = "pt" 89 | 90 | def b: BSSpacing = "pb" 91 | 92 | def x: BSSpacing = "px" 93 | 94 | def y: BSSpacing = "py" 95 | } 96 | } 97 | 98 | 99 | 100 | //GHYPHICONS 101 | lazy val glyph_edit = toGlyphicon("bi-pencil-fill") 102 | lazy val glyph_edit2 = toGlyphicon("bi-edit") 103 | lazy val glyph_save = toGlyphicon("bi-save") 104 | lazy val glyph_trash = toGlyphicon("bi-trash") 105 | lazy val glyph_plus = toGlyphicon("bi-plus") 106 | lazy val glyph_plus_sign = toGlyphicon("bi-plus-sign") 107 | lazy val glyph_minus_sign = toGlyphicon("bi-minus-sign") 108 | lazy val glyph_minus = toGlyphicon("bi-minus") 109 | lazy val glyph_ok = toGlyphicon("bi-ok") 110 | lazy val glyph_question = toGlyphicon("bi-question-sign") 111 | lazy val glyph_file = toGlyphicon("bi-file") 112 | lazy val glyph_folder_close = toGlyphicon("bi-folder-close") 113 | lazy val glyph_home = toGlyphicon("bi-home") 114 | lazy val glyph_upload = toGlyphicon("bi-cloud-upload") 115 | lazy val glyph_download = toGlyphicon("bi-download") 116 | lazy val glyph_download_alt = toGlyphicon("bi-download-alt") 117 | lazy val glyph_settings = toGlyphicon("bi-gear-fill") 118 | lazy val glyph_off = toGlyphicon("bi-off") 119 | lazy val glyph_lightning = toGlyphicon("bi-lightning") 120 | lazy val glyph_flag = toGlyphicon("bi-flag") 121 | lazy val glyph_remove = toGlyphicon("bi-x-square") 122 | lazy val glyph_road = toGlyphicon("bi-road") 123 | lazy val glyph_heart = toGlyphicon("bi-suit-heart-fill") 124 | lazy val glyph_list = toGlyphicon("bi-list") 125 | lazy val glyph_stats = toGlyphicon("bi-stats") 126 | lazy val glyph_refresh = toGlyphicon("bi-arrow-repeat") 127 | lazy val glyph_repeat = toGlyphicon("bi-repeat") 128 | lazy val glyph_lock = toGlyphicon("bi-lock-fill") 129 | lazy val glyph_archive = toGlyphicon("bi-compressed") 130 | lazy val glyph_market = toGlyphicon("bi-shopping-cart") 131 | lazy val glyph_info = toGlyphicon("bi-info-sign") 132 | lazy val glyph_plug = toGlyphicon("bi-plug-fill") 133 | lazy val glyph_exclamation = toGlyphicon("bi-exclamation-sign") 134 | lazy val glyph_comment = toGlyphicon("bi-comment") 135 | lazy val glyph_upload_alt = toGlyphicon("bi-upload") 136 | lazy val glyph_arrow_right = toGlyphicon("bi-arrow-right") 137 | lazy val glyph_arrow_left = toGlyphicon("bi-arrow-left") 138 | lazy val glyph_arrow_right_and_left = toGlyphicon("bi-resize -horizontal") 139 | lazy val glyph_filter = toGlyphicon("bi-filter") 140 | lazy val glyph_copy = toGlyphicon("bi-copy") 141 | lazy val glyph_paste = toGlyphicon("bi-paste") 142 | lazy val glyph_time = toGlyphicon("bi-time") 143 | lazy val glyph_alph_sorting = toGlyphicon("bi-sort-by-alphabet") 144 | lazy val glyph_sort_down = "bi-sort-down" 145 | lazy val glyph_sort_down_alt = "bi-sort-down-alt" 146 | lazy val glyph_triangle_down = toGlyphicon("bi-caret-down-fill") 147 | lazy val glyph_triangle_up = toGlyphicon("bi-caret-up-fill") 148 | lazy val glyph_chevron_left = toGlyphicon("bi-chevron-left") 149 | lazy val glyph_chevron_right = toGlyphicon("bi-chevron-right") 150 | lazy val glyph_menu_hamburger = toGlyphicon("bi-menu-hamburger") 151 | lazy val glyph_right_caret = toGlyphicon("bi-caret-right-fill") 152 | lazy val glyph_three_dots = toGlyphicon("bi-three-dots-vertical") 153 | 154 | //NAVBARS 155 | lazy val nav = cls("nav") 156 | lazy val navbar = cls("navbar") 157 | lazy val navbar_nav = cls("navbar-nav") 158 | lazy val navbar_expand_lg = cls("navbar-expand-lg") 159 | lazy val navbar_light = cls("navbar-light") 160 | lazy val bg_light = cls("bg-light") 161 | lazy val navTabs = cls("nav-tabs") 162 | lazy val navbar_default = cls("navbar-default") 163 | lazy val navbar_inverse = cls("navbar-inverse") 164 | lazy val navbar_fixedTop = cls("navbar-fixed-top") 165 | lazy val navbar_pills = cls("nav-pills") 166 | lazy val navbar_form = cls("navbar-form") 167 | lazy val navbar_right = cls("navbar-right") 168 | lazy val navbar_left = cls("navbar-left") 169 | lazy val navbar_header = cls("navbar-header") 170 | lazy val navbar_brand = cls("navbar-brand") 171 | lazy val navbar_btn = cls("navbar-btn") 172 | lazy val navbar_collapse = cls("collapse navbar-collapse") 173 | 174 | //LABELS 175 | lazy val badge_light = toBadge("badge-light") 176 | lazy val badge_primary = toBadge("badge-primary") 177 | lazy val badge_success = toBadge("badge-success") 178 | lazy val badge_info = toBadge("badge-info") 179 | lazy val badge_warning = toBadge("badge-warning") 180 | lazy val badge_danger = toBadge("badge-danger") 181 | lazy val badge_dark = toBadge("badge-dark") 182 | lazy val badge_secondary = toBadge("badge-secondary") 183 | 184 | lazy val controlLabel = cls("control-label") 185 | 186 | 187 | //BUTTONS 188 | lazy val btn = cls("btn") 189 | lazy val btn_secondary_string = "btn btn-secondary" 190 | lazy val btn_outline_secondary_string = "btn btn-outline-secondary" 191 | lazy val btn_secondary = toButton(s"btn $btn_secondary_string") 192 | 193 | lazy val btn_primary_string = "btn btn-primary" 194 | lazy val btn_outline_primary_string = "btn btn-outline-primary" 195 | lazy val btn_primary = toButton(s"btn $btn_primary_string") 196 | 197 | lazy val btn_light = toButton("btn btn-light") 198 | lazy val btn_light_outline = toButton("btn btn-outline-light") 199 | 200 | lazy val btn_success_string = "btn btn-success" 201 | lazy val btn_success = toButton(btn_success_string) 202 | 203 | lazy val btn_info_string = "btn btn-info" 204 | lazy val btn_info = toButton(btn_info_string) 205 | 206 | lazy val btn_warning_string = "btn btn-warning" 207 | lazy val btn_warning = toButton(btn_warning_string) 208 | 209 | lazy val btn_danger_string = "btn btn-danger" 210 | lazy val btn_danger = toButton(btn_danger_string) 211 | 212 | lazy val btn_primary_outline = toButton("btn-outline-primary") 213 | lazy val btn_secondary_outline = toButton("btn-outline-secondary") 214 | lazy val btn_success_outline = toButton("btn-outline-success") 215 | lazy val btn_info_outline = toButton("btn-outline-info") 216 | lazy val btn_warning_outline = toButton("btn-outline-warning") 217 | lazy val btn_danger_outline = toButton("btn-outline-danger") 218 | 219 | lazy val btn_large = toButton("btn-lg") 220 | lazy val btn_medium = toButton("btn-md") 221 | lazy val btn_small = toButton("btn-sm") 222 | lazy val btn_test = toButton("myButton") 223 | lazy val btn_right = toButton("pull-right") 224 | 225 | lazy val btnGroup = cls("btn-group") 226 | lazy val btnGroupToggle = cls("btn-group-toggle") 227 | lazy val btnToolbar = cls("btn-toolbar") 228 | 229 | 230 | //ALERTS 231 | lazy val alert_success = toAlert("alert-success") 232 | lazy val alert_info = toAlert("alert-info") 233 | lazy val alert_warning = toAlert("alert-warning") 234 | lazy val alert_danger = toAlert("alert-danger") 235 | 236 | //MODALS 237 | lazy val modal = cls("modal") 238 | lazy val fade = cls("fade") 239 | lazy val modalDialog = cls("modal-dialog") 240 | lazy val modalContent = cls("modal-content") 241 | lazy val modalHeader = cls("modal-header") 242 | lazy val modalInfo = cls("modal-info") 243 | lazy val modalBody = cls("modal-body") 244 | lazy val modalFooter = cls("modal-footer") 245 | 246 | //NAVS 247 | lazy val tabsClass = toNav("nav-tabs") 248 | lazy val justified_tabs = toNav("nav-tabs nav-justified") 249 | lazy val pills = toNav("nav-pills") 250 | lazy val stacked_pills = toNav("nav-pills nav-stacked") 251 | lazy val justified_pills = toNav("nav-pills nav-justified") 252 | lazy val inline_list = toNav("list-inline") 253 | lazy val nav_bar = toNav("navbar-nav") 254 | lazy val regular_nav = toNav("nav-list") 255 | lazy val panel_nav = toNav("panel panel-primary") 256 | lazy val presentation_role = role := "presentation" 257 | lazy val tab_panel_role = role := "tabpanel" 258 | lazy val tab_list_role = role := "tablist" 259 | lazy val tab_role = role := "tab" 260 | lazy val tab_content = cls("tab-content") 261 | lazy val tab_pane = cls("tab-pane") 262 | lazy val nav_item = "nav-item" 263 | lazy val nav_link = "nav-link" 264 | 265 | //TABLES 266 | lazy val header_no_color = emptyMod 267 | lazy val default_header = cls("thead-default") 268 | lazy val default_inverse = cls("thead-inverse") 269 | lazy val default_table = toTable("") 270 | lazy val inverse_table = toTable("table-inverse") 271 | lazy val striped_table = toTable("table-striped") 272 | lazy val bordered_table = toTable("table-bordered") 273 | lazy val hover_table = toTable("table-hover") 274 | 275 | //GRIDS 276 | def colBS(nbCol: Int) = cls(s"col-$nbCol") 277 | 278 | def colMDOffset(offsetSize: Int) = cls(s"col-md-offset-$offsetSize") 279 | 280 | lazy val row = cls("row") 281 | lazy val colSM = cls("col-sm") 282 | 283 | 284 | //PANELS 285 | lazy val panelClass = cls("panel") 286 | lazy val panelDefault = cls("panel-default") 287 | lazy val panelHeading = cls("panel-heading") 288 | lazy val panelTitle = cls("panel-title") 289 | lazy val panelBody = cls("panel-body") 290 | 291 | 292 | //TABLES 293 | lazy val tableClass = cls("table") 294 | lazy val bordered = cls("table-bordered") 295 | lazy val striped = cls("table-striped") 296 | lazy val active = cls("active") 297 | lazy val success = cls("success") 298 | lazy val danger = cls("danger") 299 | lazy val warning = cls("warning") 300 | lazy val info = cls("info") 301 | 302 | 303 | //INPUTS 304 | lazy val inputGroupClass = cls("input-group") 305 | lazy val inputGroupButtonClass = cls("input-group-btn") 306 | lazy val inputGroupAddonClass = cls("input-group-addon") 307 | 308 | //FORMS 309 | lazy val formControl = cls("form-control") 310 | lazy val formGroup = cls("form-group") 311 | lazy val formInline = cls("form-inline") 312 | lazy val formHorizontal = cls("form-horizontal") 313 | lazy val formVertical = cls("form-vertical") 314 | 315 | 316 | //OTHERS 317 | lazy val dropdown = cls("dropdown") 318 | lazy val dropdownMenu = cls("dropdown-menu") 319 | lazy val dropdownToggle = cls("dropdown-toggle") 320 | lazy val progress = cls("progress") 321 | lazy val progressBar = cls("progress-bar") 322 | lazy val container = cls("container") 323 | lazy val containerFluid = cls("container-fluid") 324 | lazy val jumbotron = cls("jumbotron") 325 | lazy val themeShowcase = cls("theme-showcase") 326 | lazy val controlGroup = cls("control-group") 327 | lazy val controls = cls("controls") 328 | 329 | 330 | //TOASTS 331 | lazy val toastCls = cls := "toast" 332 | lazy val toastHeader = cls := "toast-header" 333 | lazy val toastBody = cls := "toast-body" 334 | 335 | lazy val bottomRightPosition = Seq(cls := "position-fixed bottom-0 right-0 p-3", zIndex := "5", right := "0", bottom := "0") 336 | 337 | } 338 | 339 | } 340 | 341 | package stylesheet2 { 342 | 343 | package object bootstrap2 extends Bootstrap2Package 344 | 345 | trait Bootstrap2Package { 346 | 347 | //Exclusive Button Group 348 | lazy val stringInGroup = Seq( 349 | height := "30px", 350 | paddingTop := "3px", 351 | paddingLeft := "6px", 352 | paddingRight := "6px" 353 | ) 354 | 355 | lazy val twoGlyphButton = Seq( 356 | top := "1px", 357 | height := "30px" 358 | ) 359 | 360 | lazy val stringButton = Seq( 361 | top := "4px", 362 | height := "30px" 363 | ) 364 | 365 | lazy val collapseTransition = Seq( 366 | transition := "height .3s", 367 | height := "0", 368 | overflow := "hidden" 369 | ) 370 | 371 | lazy val caret = Seq( 372 | display.inlineBlock, 373 | width := "0", 374 | height := "0", 375 | marginLeft := "5", 376 | verticalAlign.middle, 377 | borderTop := "4px dashed", 378 | borderRight := "4px solid transparent ", 379 | borderLeft := "4px solid transparent" 380 | ) 381 | 382 | } 383 | 384 | } 385 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | //import execnpm.ExecNpmPlugin.autoImport._ 4 | //import execnpm.NpmDeps._ 5 | 6 | 7 | val aceVersion = "1.4.3" 8 | val aceDiffVersion = "2.3.0" 9 | val bootstrapNativeVersion = "3.0.14-f" 10 | val bootstrapIcons = "1.4.0" 11 | val bootstrapSliderVersion = "10.4.0" 12 | val nouiSliderVersion = "15.5.0" 13 | val highlightVersion = "10.4.1" 14 | val lunrVersion = "2.1.5" 15 | 16 | //2.13 17 | val jsextVersion = "0.10" 18 | val laminarVersion = "15.0.1" 19 | val scalaJSdomVersion = "2.0.0" 20 | val sourceCodeVersion = "0.2.7" 21 | val scalaJSortableVersion = "0.8" 22 | 23 | 24 | ThisBuild / organization := "org.openmole.scaladget" 25 | name := "scaladget" 26 | 27 | 28 | import sbtrelease.ReleasePlugin.autoImport.ReleaseTransformations._ 29 | 30 | ThisBuild / scalaVersion := "3.3.3" 31 | 32 | //ThisBuild / crossScalaVersions := Seq("2.13.8", "3.0.0") 33 | 34 | ThisBuild / pomIncludeRepository := { _ => false } 35 | 36 | resolvers += Resolver.sonatypeRepo("releases") 37 | 38 | ThisBuild / licenses := Seq("Affero GPLv3" -> url("http://www.gnu.org/licenses/")) 39 | 40 | ThisBuild / homepage := Some(url("https://github.com/openmole/scala-js-plotlyjs")) 41 | 42 | ThisBuild / scmInfo := Some(ScmInfo(url("https://github.com/openmole/scaladget.git"), "git@github.com:openmole/scaladget.git")) 43 | 44 | ThisBuild / pomExtra := ( 45 | 46 | 47 | mathieu.leclaire 48 | Mathieu Leclaire 49 | 50 | 51 | ) 52 | 53 | ThisBuild / releasePublishArtifactsAction := PgpKeys.publishSigned.value 54 | 55 | releaseVersionBump := sbtrelease.Version.Bump.Minor 56 | 57 | releaseTagComment := s"Releasing ${(ThisBuild / version).value}" 58 | 59 | releaseCommitMessage := s"Bump version to ${(ThisBuild / version).value}" 60 | 61 | sonatypeProfileName := "org.openmole" 62 | 63 | 64 | ThisBuild / publishConfiguration := publishConfiguration.value.withOverwrite(true) 65 | 66 | ThisBuild / publishTo := sonatypePublishToBundle.value 67 | 68 | ThisBuild / publishMavenStyle := true 69 | 70 | ThisBuild / releaseCrossBuild := true 71 | 72 | 73 | releaseProcess := Seq[ReleaseStep]( 74 | checkSnapshotDependencies, 75 | inquireVersions, 76 | runClean, 77 | setReleaseVersion, 78 | tagRelease, 79 | releaseStepCommandAndRemaining("+publishSigned"), 80 | releaseStepCommand("sonatypeBundleRelease"), 81 | setNextVersion, 82 | commitNextVersion, 83 | pushChanges 84 | ) 85 | 86 | lazy val scalaJsDom = libraryDependencies += "org.scala-js" %%% "scalajs-dom" % scalaJSdomVersion 87 | lazy val laminar = libraryDependencies += "com.raquo" %%% "laminar" % laminarVersion 88 | lazy val sourceCode = libraryDependencies += "com.lihaoyi" %%% "sourcecode" % sourceCodeVersion 89 | lazy val jsext = libraryDependencies += "org.querki" %%% "querki-jsext" % jsextVersion cross (CrossVersion.for3Use2_13) 90 | 91 | lazy val ace = project.in(file("ace")) enablePlugins (ScalaJSBundlerPlugin) settings( 92 | scalaJsDom, 93 | jsext, 94 | Compile / npmDependencies += "ace-builds" -> aceVersion 95 | ) dependsOn (bootstrapnative) 96 | // 97 | //lazy val aceDiff = project.in(file("acediff")) enablePlugins (ScalaJSBundlerPlugin) dependsOn (ace) settings( 98 | // scalaJsDom, 99 | // jsext, 100 | // npmDeps in Compile += Dep("ace-diff", aceDiffVersion, List("ace-diff.min.js", "ace-diff.min.css"), true) 101 | //) 102 | // 103 | lazy val nouislider = project.in(file("nouislider")) enablePlugins (ScalaJSBundlerPlugin) settings( 104 | scalaJsDom, 105 | jsext, 106 | laminar, 107 | Compile / npmDependencies += "nouislider"-> nouiSliderVersion 108 | ) 109 | 110 | // 111 | lazy val bootstrapnative = project.in(file("bootstrapnative")) enablePlugins (ScalaJSBundlerPlugin) settings( 112 | scalaJsDom, 113 | // libraryDependencies += "org.openmole" %%% "sortable-js-facade" % scalaJSortableVersion, 114 | laminar, 115 | Compile / npmDependencies += "bootstrap.native" -> bootstrapNativeVersion, 116 | //npmDependencies in Compile += "sortablejs" -> sortableVersion 117 | ) dependsOn (tools) 118 | // 119 | // 120 | lazy val highlightjs = project.in(file("highlightjs")) enablePlugins (ScalaJSBundlerPlugin) settings( 121 | jsext, 122 | scalaJsDom, 123 | Compile / npmDependencies += "highlight.js" -> highlightVersion 124 | ) 125 | 126 | lazy val lunr = project.in(file("lunr")) enablePlugins (ScalaJSBundlerPlugin) settings ( 127 | Compile / npmDependencies += "lunr" -> lunrVersion 128 | ) 129 | 130 | lazy val svg = project.in(file("svg")) enablePlugins (ScalaJSPlugin) settings( 131 | laminar, 132 | scalaJsDom 133 | ) dependsOn (tools) 134 | 135 | 136 | lazy val tools = project.in(file("tools")) enablePlugins (ScalaJSBundlerPlugin) settings( 137 | scalaJsDom, 138 | laminar 139 | ) 140 | 141 | lazy val runBootstrapDemo = taskKey[Unit]("runBootsrapDemo") 142 | 143 | lazy val bootstrapDemo = project.in(file("bootstrapDemo")) enablePlugins (ScalaJSBundlerPlugin) settings( 144 | publishArtifact := false, 145 | publish := {}, 146 | publishLocal := {}, 147 | test := println("Tests disabled"), 148 | laminar, 149 | sourceCode, 150 | scalaJSLinkerConfig := scalaJSLinkerConfig.value.withSourceMap(false), 151 | scalaJSUseMainModuleInitializer := true, 152 | webpackNodeArgs := Seq("--openssl-legacy-provider"), 153 | Test / requireJsDomEnv := true, 154 | runBootstrapDemo := { 155 | val demoResource = (Compile / resourceDirectory).value 156 | val jsBuild = (Compile / fastOptJS / webpack).value.head.data 157 | 158 | IO.copyFile(jsBuild, target.value / "js/demobootstrapnative.js") 159 | IO.copyDirectory(demoResource, target.value) 160 | 161 | } 162 | ) dependsOn(ace, bootstrapnative, tools, highlightjs, nouislider, lunr) 163 | 164 | 165 | lazy val runSVGDemo = taskKey[Unit]("runSVGDemo") 166 | 167 | lazy val svgDemo = project.in(file("svgDemo")) enablePlugins (ScalaJSBundlerPlugin) settings( 168 | publishArtifact := false, 169 | publish := {}, 170 | publishLocal := {}, 171 | test := println("Tests disabled"), 172 | laminar, 173 | sourceCode, 174 | scalaJSUseMainModuleInitializer := true, 175 | Test / requireJsDomEnv := true, 176 | runSVGDemo := { 177 | val demoResource = (Compile / resourceDirectory).value 178 | val jsBuild = (Compile / fastOptJS / webpack).value.head.data 179 | 180 | IO.copyFile(jsBuild, target.value / "js/demosvg.js") 181 | IO.copyDirectory(demoResource, target.value) 182 | } 183 | ) dependsOn(svg, tools, bootstrapnative, highlightjs) 184 | 185 | 186 | lazy val runFlowchartDemo = taskKey[Unit]("runFlowchartDemo") 187 | 188 | lazy val flowchartDemo = project.in(file("flowchartDemo")) enablePlugins (ScalaJSBundlerPlugin) settings( 189 | publishArtifact := false, 190 | publish := {}, 191 | publishLocal := {}, 192 | test := println("Tests disabled"), 193 | laminar, 194 | sourceCode, 195 | scalaJSUseMainModuleInitializer := true, 196 | Test / requireJsDomEnv := true, 197 | runFlowchartDemo := { 198 | val demoResource = (Compile / resourceDirectory).value 199 | val jsBuild = (Compile / fastOptJS / webpack).value.head.data 200 | 201 | IO.copyFile(jsBuild, target.value / "js/flowchart.js") 202 | IO.copyDirectory(demoResource, target.value) 203 | } 204 | ) dependsOn(svg, tools, bootstrapnative) 205 | -------------------------------------------------------------------------------- /flowchartDemo/src/main/resources/css/github-gist.css: -------------------------------------------------------------------------------- 1 | /** 2 | * GitHub Gist Theme 3 | * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | background: white; 9 | padding: 0.5em; 10 | color: #333333; 11 | overflow-x: auto; 12 | } 13 | 14 | .hljs-comment, 15 | .hljs-meta { 16 | color: #969896; 17 | } 18 | 19 | .hljs-string, 20 | .hljs-variable, 21 | .hljs-template-variable, 22 | .hljs-strong, 23 | .hljs-emphasis, 24 | .hljs-quote { 25 | color: #df5000; 26 | } 27 | 28 | .hljs-keyword, 29 | .hljs-selector-tag, 30 | .hljs-type { 31 | color: #a71d5d; 32 | } 33 | 34 | .hljs-literal, 35 | .hljs-symbol, 36 | .hljs-bullet, 37 | .hljs-attribute { 38 | color: #0086b3; 39 | } 40 | 41 | .hljs-section, 42 | .hljs-name { 43 | color: #63a35c; 44 | } 45 | 46 | .hljs-tag { 47 | color: #333333; 48 | } 49 | 50 | .hljs-title, 51 | .hljs-attr, 52 | .hljs-selector-id, 53 | .hljs-selector-class, 54 | .hljs-selector-attr, 55 | .hljs-selector-pseudo { 56 | color: #795da3; 57 | } 58 | 59 | .hljs-addition { 60 | color: #55a532; 61 | background-color: #eaffea; 62 | } 63 | 64 | .hljs-deletion { 65 | color: #bd2c00; 66 | background-color: #ffecec; 67 | } 68 | 69 | .hljs-link { 70 | text-decoration: underline; 71 | } 72 | -------------------------------------------------------------------------------- /flowchartDemo/src/main/resources/css/styleGraph.css: -------------------------------------------------------------------------------- 1 | 2 | body{ 3 | margin: 0; 4 | padding: 0; 5 | overflow:hidden; 6 | } 7 | 8 | p{ 9 | text-align: center; 10 | overflow: overlay; 11 | position: relative; 12 | } 13 | 14 | body{ 15 | -webkit-touch-callout: none; 16 | -webkit-user-select: none; 17 | -khtml-user-select: none; 18 | -moz-user-select: none; 19 | -ms-user-select: none; 20 | user-select: none; 21 | background-color: rgb(248, 248, 248) 22 | } 23 | 24 | #toolbox{ 25 | position: absolute; 26 | bottom: 0; 27 | left: 0; 28 | margin-bottom: 0.5em; 29 | margin-left: 1em; 30 | border: 2px solid #EEEEEE; 31 | border-radius: 5px; 32 | padding: 1em; 33 | z-index: 5; 34 | } 35 | 36 | #toolbox input{ 37 | width: 30px; 38 | opacity: 0.4; 39 | } 40 | #toolbox input:hover{ 41 | opacity: 1; 42 | cursor: pointer; 43 | } 44 | 45 | #hidden-file-upload{ 46 | display: none; 47 | } 48 | 49 | #download-input{ 50 | margin: 0 0.5em; 51 | } 52 | 53 | .conceptG text{ 54 | pointer-events: none; 55 | } 56 | 57 | marker{ 58 | fill: #333; 59 | } 60 | 61 | g.conceptG circle{ 62 | fill: #F6FBFF; 63 | stroke: #333; 64 | stroke-width: 2px; 65 | z-index: 21; 66 | } 67 | 68 | g.conceptG:hover circle{ 69 | fill: rgb(200, 238, 1); 70 | } 71 | 72 | g.selected circle{ 73 | fill: rgb(0, 232, 255); 74 | } 75 | g.selected:hover circle{ 76 | fill: rgb(250, 232, 255); 77 | } 78 | 79 | path.link { 80 | fill: none; 81 | stroke: #333; 82 | stroke-width: 6px; 83 | cursor: default; 84 | } 85 | 86 | path.link:hover{ 87 | stroke: rgb(94, 196, 204); 88 | } 89 | 90 | g.connect-node circle{ 91 | fill: #BEFFFF; 92 | } 93 | 94 | path.link.hidden{ 95 | stroke-width: 0; 96 | } 97 | 98 | path.link.selected { 99 | stroke: rgb(229, 172, 247); 100 | } 101 | -------------------------------------------------------------------------------- /flowchartDemo/src/main/resources/flowchart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /flowchartDemo/src/main/resources/img/svgstar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/flowchartDemo/src/main/resources/img/svgstar.png -------------------------------------------------------------------------------- /flowchartDemo/src/main/scala/demo/FlowChart.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | /* 4 | * Copyright (C) 22/09/14 // mathieu.leclaire@openmole.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import java.util.UUID 21 | import scaladget.bootstrapnative.bsn.* 22 | import com.raquo.laminar.api.L.* 23 | import com.raquo.laminar.api.L.svg 24 | import org.scalajs 25 | import org.scalajs.dom.{KeyboardEvent, MouseEvent, raw} 26 | 27 | import scala.scalajs.js.Dynamic 28 | 29 | trait Selectable { 30 | val selected: Var[Boolean] = Var(false) 31 | } 32 | 33 | // DEFINE SOME CASE CLASS TO STORE TASK AND EDGE STRUCTURES 34 | object Graph { 35 | 36 | case class Task(title: Var[String] = Var(""), 37 | location: Var[(Double, Double)] = Var((0.0, 0.0))) extends Selectable 38 | 39 | class Edge(val source: Var[Task], 40 | val target: Var[Task]) extends Selectable 41 | 42 | def task(title: String, x: Double, y: Double) = Task(Var(title), Var((x, y))) 43 | 44 | def edge(source: Task, target: Task) = new Edge(Var(source), Var(target)) 45 | } 46 | 47 | import demo.Graph._ 48 | 49 | class GraphCreator(_tasks: Seq[Task], _edges: Seq[Edge]) { 50 | 51 | val SELECTED: String = "selected" 52 | val CIRCLE: String = "conceptG" 53 | val LINK: String = "link" 54 | val LINK_DRAGLINE: String = "link dragline" 55 | val HIDDEN: String = "hidden" 56 | val DELETE_KEY = 46 57 | val NODE_RADIUS = 50 58 | val END_ARROW: String = "end-arrow" 59 | val URL_END_ARROW: String = s"url(#${END_ARROW})" 60 | val MARK_END_ARROW: String = "mark-end-arrow" 61 | val URL_MARK_END_ARROW: String = s"url(#${MARK_END_ARROW})" 62 | 63 | // DEFINE A SVG ELEMENT TO DISPLAY A PHANTOM LINK WHILE DRAGGING A LINK FROM ONE TASK TO ANOTHER 64 | class DragLine { 65 | private val m: Var[(Int, Int)] = Var((0, 0)) 66 | private val l: Var[(Int, Int)] = Var((0, 0)) 67 | val dragging = Var(false) 68 | 69 | def move(x: Int, y: Int) = { 70 | dragging.set(true) 71 | m.set((x, y)) 72 | this 73 | } 74 | 75 | def line(x: Int, y: Int) = { 76 | dragging.set(true) 77 | l.set((x, y)) 78 | this 79 | } 80 | 81 | val render = { 82 | svg.path( 83 | svg.d <-- m.signal.combineWith(l.signal).map { case (mx, my, lx, ly) => s"M $mx $my L $lx $ly" }, 84 | svg.markerEnd := URL_MARK_END_ARROW, 85 | svg.cls := LINK_DRAGLINE, 86 | svg.cls.toggle(HIDDEN) <-- dragging.signal.map { 87 | !_ 88 | } 89 | ) 90 | } 91 | } 92 | 93 | implicit def dynamicToString(d: Dynamic): String = d.asInstanceOf[String] 94 | 95 | implicit def dynamicToBoolean(d: Dynamic): Boolean = d.asInstanceOf[Boolean] 96 | 97 | // SVG DEFINITIONS 98 | lazy val dragLine = new DragLine 99 | 100 | val tasks: Var[Seq[Task]] = Var(Seq()) 101 | _tasks.map { 102 | addTask 103 | } 104 | 105 | val edges: Var[Seq[Edge]] = Var(Seq()) 106 | _edges.map { e => 107 | addEdge(edge(e.source.now(), e.target.now())) 108 | } 109 | 110 | val svgG = svg.g( 111 | dragLine.render, 112 | svg.g( 113 | children <-- tasks.signal.combineWith(edges).map { case (t, e) => 114 | e.map(link) ++ t.map(circle) 115 | } 116 | ) 117 | ) 118 | 119 | val mouseDownTask: Var[Option[Task]] = Var(None) 120 | val dragging: Var[Option[DragLine]] = Var(None) 121 | 122 | 123 | def mousemove(me: MouseEvent) = { 124 | Seq(mouseDownTask.now()).flatten.map { 125 | t ⇒ 126 | val x = me.clientX 127 | val y = me.clientY 128 | if (me.shiftKey) { 129 | dragLine.move(t.location.now()._1.toInt, t.location.now()._2.toInt).line(x.toInt, y.toInt) 130 | } 131 | else { 132 | t.location.set((x, y)) 133 | } 134 | } 135 | } 136 | 137 | def mouseup(me: MouseEvent) = { 138 | // Hide the drag line 139 | if (me.shiftKey && !dragLine.dragging.now()) { 140 | val (x, y) = (me.clientX, me.clientY) 141 | addTask(task(UUID.randomUUID().toString, x, y)) 142 | } 143 | mouseDownTask.set(None) 144 | dragLine.dragging.set(false) 145 | } 146 | 147 | // ARROW MARKERS FOR GRAPH LINKS 148 | def arrow = svg.marker( 149 | svg.viewBox := "0 -5 10 10", 150 | svg.markerWidth := "3.5", 151 | svg.markerHeight := "3.5", 152 | svg.orient := "auto", 153 | svg.refX := "32" 154 | ) 155 | 156 | def endArrowMarker = arrow.amend( 157 | svg.idAttr := END_ARROW, 158 | svg.refX := "32", 159 | scaladget.svg.path.start(0, -5).l(10, 0).l(0, 5).render 160 | ) 161 | 162 | def markEndArrow = arrow.amend( 163 | svg.idAttr := MARK_END_ARROW, 164 | svg.refX := "7", 165 | scaladget.svg.path.start(0, -5).l(10, 0).l(0, 5).render 166 | ) 167 | 168 | val defs = svg.defs( 169 | endArrowMarker, 170 | markEndArrow 171 | ) 172 | 173 | // RETURN A SVG CIRCLE, WHICH CAN BE SELECTED (ON CLICK), MOVED OR DELETED (DEL KEY) 174 | def circle(task: Task) = { 175 | val element: SvgElement = 176 | svg.g( 177 | svg.cls := CIRCLE, 178 | svg.cls.toggle((SELECTED)) <-- task.selected.signal, 179 | svg.transform <-- task.location.signal.map { 180 | l => 181 | s"translate(${ 182 | l._1 183 | },${ 184 | l._2 185 | })" 186 | }, 187 | svg.circle(svg.r := NODE_RADIUS.toString) 188 | ) 189 | 190 | 191 | val gCircle = svg.g( 192 | element, 193 | onMouseDown --> { 194 | me => 195 | mouseDownTask.set(Some(task)) 196 | me.stopPropagation() 197 | unselectTasks 198 | unselectEdges 199 | task.selected.update(!_) 200 | }, 201 | onMouseUp --> { 202 | _ => 203 | Seq(mouseDownTask.now()).flatten.map { 204 | mdt ⇒ 205 | if (task != mdt) { 206 | addEdge(edge(mdt, task)) 207 | } 208 | } 209 | } 210 | ) 211 | gCircle 212 | } 213 | 214 | // DEFINE A LINK, WHICH CAN BE SELECTED AND REMOVED (DEL KEY) 215 | def link(edge: Edge) = 216 | svg.g( 217 | svg.path( 218 | svg.d <-- edge.source.signal.map(_.location).combineWith(edge.target.signal.map(_.location)).map { 219 | case (source, target) => 220 | source.signal.combineWith(target.signal).map { 221 | case (sx, sy, tx, ty) => 222 | s"M $sx $sy L $tx $ty" 223 | } 224 | }.flatten, 225 | svg.markerEnd := URL_END_ARROW, 226 | svg.cls := LINK, 227 | svg.cls.toggle(SELECTED) <-- edge.selected.signal, 228 | onMouseDown --> { 229 | _ => 230 | unselectTasks 231 | unselectEdges 232 | edge.selected.update(!_) 233 | } 234 | ) 235 | ) 236 | 237 | val svgNode = svg.svg( 238 | svg.width := "2500", 239 | svg.height := "2500", 240 | defs, 241 | svgG, 242 | onMouseMove --> (me => mousemove(me)), 243 | onMouseUp --> (me => mouseup(me)) 244 | ) 245 | 246 | // DEAL WITH DEL KEY ACTION 247 | scalajs.dom.document.onkeydown = (e: KeyboardEvent) => { 248 | println("Pressed " + e.keyCode) 249 | e.keyCode match { 250 | case DELETE_KEY ⇒ 251 | tasks.now().filter(t ⇒ t.selected.now()).map(t ⇒ removeTask(t)) 252 | edges.now().filter(e ⇒ e.selected.now()).map(e ⇒ removeEdge(e)) 253 | case _ ⇒ 254 | } 255 | } 256 | 257 | // ADD, SELECT AND REMOVE ITEMS 258 | def unselectTasks = tasks.now().foreach { 259 | t ⇒ t.selected.set(false) 260 | } 261 | 262 | def unselectEdges = edges.now().foreach { 263 | e ⇒ e.selected.set(false) 264 | } 265 | 266 | def removeTask(t: Task) = { 267 | tasks.update(ts => ts diff Seq(t)) 268 | edges.update { 269 | es => es.filterNot(e ⇒ e.source.now() == t || e.target.now() == t) 270 | } 271 | } 272 | 273 | def removeEdge(e: Edge) = edges.update(es => es diff Seq(e)) 274 | 275 | def addTask(task: Task): Unit = tasks.update(ts => ts :+ task) 276 | 277 | def addEdge(edge: Edge): Unit = edges.update(es => es :+ edge) 278 | } -------------------------------------------------------------------------------- /flowchartDemo/src/main/scala/demo/FlowChartDemo.scala: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import scaladget.bootstrapnative.bsn._ 4 | import com.raquo.laminar.api.L._ 5 | import org.scalajs 6 | 7 | /* 8 | * Copyright (C) 24/03/16 // mathieu.leclaire@openmole.org 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | */ 22 | 23 | object App { 24 | 25 | def main(args: Array[String]): Unit = { 26 | 27 | val nodes = Seq( 28 | Graph.task("one", 400, 600), 29 | Graph.task("two", 1000, 600), 30 | Graph.task("three", 400, 100), 31 | Graph.task("four", 1000, 100), 32 | Graph.task("five", 105, 60) 33 | ) 34 | val edges = Seq( 35 | Graph.edge(nodes(0), nodes(1)), 36 | Graph.edge(nodes(0), nodes(2)), 37 | Graph.edge(nodes(3), nodes(1)), 38 | Graph.edge(nodes(3), nodes(2))) 39 | val graphCreator = new GraphCreator(nodes, edges) 40 | 41 | 42 | val notesCSS = Seq( 43 | width := "350", 44 | height.auto, 45 | position.fixed, 46 | float.right, 47 | right := "50", 48 | fontWeight.bold, 49 | backgroundColor := "#ffdd55", 50 | padding := "20", 51 | top := "10", 52 | borderRadius := "5px" 53 | ) 54 | 55 | val notes = div( 56 | notesCSS, 57 | "The demo provides with a small SVG graph editor based on the d3 library ", 58 | a(href := "https://bl.ocks.org/cjrd/6863459", target := "_blank", "http://bl.ocks.org/cjrd/6863459 "), 59 | "but with no D3.js at all.", 60 | div(paddingTop := "10", "It's fully based on ", 61 | a(href := "https://github.com/raquo/Laminar", target := "_blank", "Laminar "), 62 | ", ", 63 | a(href := "https://github.com/scala-js/scala-js-dom", target := "_blank", "scala-js-dom "), 64 | " and ", 65 | a(href := "https://github.com/openmole/scaladget", target := "_blank", "scaladget. "), 66 | "The full code can be found ", 67 | a(href := "https://github.com/openmole/scaladget/blob/master/demo/src/main/scala/fr/iscpif/demo/FlowChart.scala", target := "_blank", "here. "), 68 | "Try to:", 69 | ul( 70 | li("drag the nodes to move them"), 71 | li("shift-click on the graph to create a node"), 72 | li("shift-click on a node and then drag to another node to connect them with a directed edge"), 73 | li("click on any node or edge and press delete to remove them") 74 | ) 75 | ) 76 | ) 77 | 78 | documentEvents(_.onDomContentLoaded).foreach { _ => 79 | render(scalajs.dom.document.body, div(notes, graphCreator.svgNode)) 80 | }(unsafeWindowOwner) 81 | 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /highlightjs/src/main/scala/scaladget/highlightjs/HighlightJS.scala: -------------------------------------------------------------------------------- 1 | package scaladget.highlightjs 2 | 3 | import org.querki.jsext 4 | import org.scalajs.dom.Element 5 | 6 | import jsext._ 7 | import scala.scalajs.js 8 | import scala.scalajs.js.annotation._ 9 | 10 | /** 11 | * @see [[https://highlightjs.org/usage/]] 12 | */ 13 | 14 | 15 | 16 | @js.native 17 | @JSImport("highlight.js", JSImport.Namespace) 18 | object HighlightJS extends HighlightStatic 19 | 20 | @js.native 21 | @JSImport("highlight.js/lib/languages/scala.js", JSImport.Namespace) 22 | object scalamode extends js.Object 23 | 24 | @js.native 25 | trait HighlightStatic extends js.Object { 26 | /** 27 | * Core highlighting function. Accepts a language name, or an alias, and a string with the code to highlight. The ignore_illegals parameter, when present and evaluates to a true value, forces highlighting to finish even in case of detecting illegal syntax for the language instead of throwing an exception. The continuation is an optional mode stack representing unfinished parsing. When present, the function will restart parsing from this state instead of initializing a new one. 28 | * 29 | * @return Returns an object with the following properties: 30 | * language: language name, same as the one passed into a function, returned for consistency with highlightAuto 31 | * relevance: integer value 32 | * value: HTML string with highlighting markup 33 | * top: top of the current mode stack 34 | */ 35 | def highlight(name: String, value: String, ignoreIllegals: js.UndefOr[Boolean] = js.native, continuation: js.UndefOr[js.Object] = js.native): HighlightJSResult = js.native 36 | 37 | /** 38 | * Highlighting with language detection. Accepts a string with the code to highlight and an optional array of language names and aliases restricting detection to only those languages. The subset can also be set with configure, but the local parameter overrides the option if set. 39 | * 40 | * @return Returns an object with the following properties: 41 | * language: detected language 42 | * relevance: integer value 43 | * value: HTML string with highlighting markup 44 | * second_best: object with the same structure for second-best heuristically detected language, may be absent 45 | */ 46 | def highlightAuto(value: String, languageSubset: js.UndefOr[js.Array[String]] = js.native): HighlightJSResult = js.native 47 | 48 | 49 | /** 50 | * Applies highlighting to all `
..
` blocks on a page. 51 | */ 52 | def initHighlighting(): Unit = js.native 53 | 54 | /** 55 | * Attaches highlighting to the page load event. 56 | */ 57 | def initHighlightingOnLoad(): Unit = js.native 58 | 59 | /** 60 | * Applies highlighting to a DOM node containing code. 61 | * This function is the one to use to apply highlighting dynamically after page load or within initialization code of third-party Javascript frameworks. 62 | * The function uses language detection by default but you can specify the language in the class attribute of the DOM node. See the class reference for all available language names and aliases. 63 | */ 64 | def highlightBlock(el: Element):Unit = js.native 65 | 66 | /** 67 | * Post-processing of the highlighted markup. Currently consists of replacing indentation TAB characters and using
tags instead of new-line characters. Options are set globally with configure. 68 | * Accepts a string with the highlighted markup. 69 | */ 70 | val fixMarkup: js.UndefOr[String] = js.native 71 | 72 | /** 73 | * Configures global options: 74 | * * 75 | * tabReplace: a string used to replace TAB characters in indentation. 76 | * useBR: a flag to generate
tags instead of new-line characters in the output, useful when code is marked up using a non-
 container.
 77 |     * classPrefix: a string prefix added before class names in the generated markup, used for backwards compatibility with stylesheets.
 78 |     * languages: an array of language names and aliases restricting auto detection to only these languages.
 79 |     * Accepts an object representing options with the values to updated. Other options don’t change
 80 |     * {{{
 81 |     *   hljs.configure({
 82 |     *     tabReplace: '    ', // 4 spaces
 83 |     *     classPrefix: ''     // don't append class prefix
 84 |     *                         // … other options aren't changed
 85 |     *   });
 86 |     *   hljs.initHighlighting();
 87 |     * }}}
 88 |     */
 89 |   val configure: js.UndefOr[js.Object] = js.native
 90 | 
 91 | 
 92 |   /**
 93 |     * Adds new language to the library under the specified name. Used mostly internally.
 94 |     *
 95 |     * @param name     A string with the name of the language being registered
 96 |     * @param language A function that returns an object which represents the language definition. The function is passed the hljs object to be able to use common regular expressions defined within it.
 97 |     */
 98 |   def registerLanguage(name: String, language: js.Function): Unit = js.native
 99 | 
100 |   /**
101 |     * Returns the languages names list.
102 |     */
103 |   def listLanguages(): js.Array[String] = js.native
104 | 
105 |   /**
106 |     * Looks up a language by name or alias.
107 |     * Returns the language object if found, `undefined` otherwise.
108 |     */
109 |   def getLanguage(name: String): js.UndefOr[js.Object] = js.native
110 | }
111 | 
112 | 
113 | object HighlightStatic extends HighlightStaticBuilder(noOpts)
114 | 
115 | class HighlightStaticBuilder(val dict: OptMap) extends JSOptionBuilder[HighlightStatic, HighlightStaticBuilder](new HighlightStaticBuilder(_)) {
116 | 
117 |   def fixMarkup(v: String) = jsOpt("fixMarkup", v)
118 | 
119 |   def configure(v: js.Object) = jsOpt("configure", v)
120 | 
121 | 
122 | }
123 | 
124 | @js.native
125 | trait HighlightJSResult extends js.Object {
126 |   /**
127 |     * Detected language
128 |     */
129 |   def language: String = js.native
130 | 
131 |   /**
132 |     * Integer value
133 |     */
134 |   def relevance: Int = js.native
135 | 
136 |   /**
137 |     * HTML string with highlighting markup
138 |     */
139 |   def value: String = js.native
140 | 
141 |   /**
142 |     * Top of the current mode stack
143 |     */
144 |   def top: js.Object = js.native
145 | 
146 | //  /**
147 | //    * Object with the same structure for second-best heuristically detected language, may be absent
148 | //    */
149 | //  @JSName("second_best")
150 | //  def secondBest: js.Object = js.native
151 | }
152 | 


--------------------------------------------------------------------------------
/lunr/src/main/scala/scaladget/lunr/lunr.scala:
--------------------------------------------------------------------------------
  1 | package scaladget.lunr
  2 | 
  3 | /*
  4 |  * Copyright (C) 06/07/16 // mathieu.leclaire@openmole.org
  5 |  *
  6 |  * This program is free software: you can redistribute it and/or modify
  7 |  * it under the terms of the GNU Affero General Public License as published by
  8 |  * the Free Software Foundation, either version 3 of the License, or
  9 |  * (at your option) any later version.
 10 |  *
 11 |  * This program is distributed in the hope that it will be useful,
 12 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 |  * GNU Affero General Public License for more details.
 15 |  *
 16 |  * You should have received a copy of the GNU General Public License
 17 |  * along with this program.  If not, see .
 18 |  */
 19 | 
 20 | 
 21 | import scala.scalajs.js
 22 | import scala.scalajs.js.annotation._
 23 | 
 24 | @js.native
 25 | trait EventEmitter extends js.Object {
 26 |   def addListener(eventName: String, handler: js.Function): Unit = js.native
 27 | 
 28 |   def addListener(eventName: String, eventName2: String, handler: js.Function): Unit = js.native
 29 | 
 30 |   def addListener(eventName: String, eventName2: String, eventName3: String, handler: js.Function): Unit = js.native
 31 | 
 32 |   def addListener(eventName: String, eventName2: String, eventName3: String, eventName4: String, handler: js.Function): Unit = js.native
 33 | 
 34 |   def addListener(eventName: String, eventName2: String, eventName3: String, eventName4: String, eventName5: String, handler: js.Function): Unit = js.native
 35 | 
 36 |   def removeListener(eventName: String, handler: js.Function): Unit = js.native
 37 | 
 38 |   def emit(eventName: String, args: js.Any*): Unit = js.native
 39 | 
 40 |   def hasHandler(eventName: String): Boolean = js.native
 41 | }
 42 | 
 43 | @js.native
 44 | trait IPipelineFunction extends js.Object {
 45 |   def apply(token: String): String = js.native
 46 | 
 47 |   def apply(token: String, tokenIndex: Double): String = js.native
 48 | 
 49 |   def apply(token: String, tokenIndex: Double, tokens: js.Array[String]): String = js.native
 50 | }
 51 | 
 52 | @js.native
 53 | trait Pipeline extends js.Object {
 54 |   def registeredFunctions: js.Dictionary[js.Function] = js.native
 55 | 
 56 |   def registerFunction(fn: IPipelineFunction, label: String): Unit = js.native
 57 | 
 58 |   def warnIfFunctionNotRegistered(fn: IPipelineFunction): Unit = js.native
 59 | 
 60 |   def add(functions: IPipelineFunction*): Unit = js.native
 61 | 
 62 |   def after(existingFn: IPipelineFunction, newFn: IPipelineFunction): Unit = js.native
 63 | 
 64 |   def before(existingFn: IPipelineFunction, newFn: IPipelineFunction): Unit = js.native
 65 | 
 66 |   def remove(fn: IPipelineFunction): Unit = js.native
 67 | 
 68 |   def run(tokens: js.Array[String]): js.Array[String] = js.native
 69 | 
 70 |   def reset(): Unit = js.native
 71 | 
 72 |   def toJSON(): js.Dynamic = js.native
 73 | 
 74 |   def load(serialised: js.Any): Pipeline = js.native
 75 | }
 76 | 
 77 | @js.native
 78 | trait Vector extends js.Object {
 79 |   def list: Node = js.native
 80 | 
 81 |   def magnitude(): Double = js.native
 82 | 
 83 |   def dot(otherVector: Vector): Double = js.native
 84 | 
 85 |   def similarity(otherVector: Vector): Double = js.native
 86 | }
 87 | 
 88 | @js.native
 89 | trait Node extends js.Object {
 90 |   def idx: Double = js.native
 91 | 
 92 |   def `val`: Double = js.native
 93 | 
 94 |   def next: Node = js.native
 95 | }
 96 | 
 97 | @js.native
 98 | trait SortedSet[T] extends js.Object {
 99 |   def elements: js.Array[T] = js.native
100 | 
101 |   def length: Double = js.native
102 | 
103 |   def add(values: T*): Unit = js.native
104 | 
105 |   def toArray(): js.Array[T] = js.native
106 | 
107 |   def map(fn: js.Function, ctx: js.Any): js.Array[T] = js.native
108 | 
109 |   def forEach(fn: js.Function, ctx: js.Any): js.Dynamic = js.native
110 | 
111 |   def indexOf(elem: T, start: Double, end: Double): Double = js.native
112 | 
113 |   def locationFor(elem: T, start: Double, end: Double): Double = js.native
114 | 
115 |   def intersect(otherSet: SortedSet[T]): SortedSet[T] = js.native
116 | 
117 |   def union(otherSet: SortedSet[T]): SortedSet[T] = js.native
118 | 
119 |   //def clone(): SortedSet[T] = js.native
120 | 
121 |   def toJSON(): js.Dynamic = js.native
122 | 
123 |   def load[T](serialisedData: js.Array[T]): SortedSet[T] = js.native
124 | }
125 | 
126 | @js.native
127 | trait IIndexField extends js.Object {
128 |   def name: js.UndefOr[String] = js.native
129 | 
130 |   def boost: js.UndefOr[Double] = js.native
131 | }
132 | 
133 | @js.native
134 | trait IIndexSearchResult extends js.Object {
135 |   def ref: String = js.native
136 | 
137 |   def score: Double = js.native
138 | }
139 | 
140 | @js.native
141 | trait Index extends js.Object {
142 | 
143 |   def min: js.UndefOr[Double] = js.native
144 | 
145 |   def eventEmitter: js.UndefOr[EventEmitter] = js.native
146 | 
147 |   def documentStore: Store[String] = js.native
148 | 
149 |   def tokenStore: TokenStore = js.native
150 | 
151 |   def corpusTokens: SortedSet[String] = js.native
152 | 
153 |   def pipeline: Pipeline = js.native
154 | 
155 |   def _fields: js.Array[IIndexField] = js.native
156 | 
157 |   def _ref: js.Array[String] = js.native
158 | 
159 |   def _idfCache: js.Array[js.Dictionary[String]] = js.native
160 | 
161 |   def on(eventName: String, handler: js.Function): Unit = js.native
162 | 
163 |   def on(eventName: String, eventName2: String, handler: js.Function): Unit = js.native
164 | 
165 |   def on(eventName: String, eventName2: String, eventName3: String, handler: js.Function): Unit = js.native
166 | 
167 |   def on(eventName: String, eventName2: String, eventName3: String, eventName4: String, handler: js.Function): Unit = js.native
168 | 
169 |   def on(eventName: String, eventName2: String, eventName3: String, eventName4: String, eventName5: String, handler: js.Function): Unit = js.native
170 | 
171 |   def off(eventName: String, handler: js.Function): Unit = js.native
172 | 
173 |   def field(fieldName: String, options: js.Any): Index = js.native
174 | 
175 |   def ref(refName: String): Index = js.native
176 | 
177 |   def add(doc: js.Any, emitEvent: Boolean = false): Unit = js.native
178 | 
179 |   def remove(doc: js.Any, emitEvent: Boolean = false): Unit = js.native
180 | 
181 |   def update(doc: js.Any, emitEvent: Boolean = false): Unit = js.native
182 | 
183 |   def idf(token: String): String = js.native
184 | 
185 |   def search(query: String): js.Array[IIndexSearchResult] = js.native
186 | 
187 |   def documentVector(documentRef: String): Vector = js.native
188 | 
189 |   def toJSON(): js.Dynamic = js.native
190 | 
191 |   def use(plugin: js.Function, args: js.Any*): Unit = js.native
192 | 
193 |   def load(serialisedData: js.Any): Index = js.native
194 | }
195 | 
196 | @js.native
197 | trait Store[T] extends js.Object {
198 |   def store: js.Dictionary[SortedSet[T]] = js.native
199 | 
200 |   def length: Double = js.native
201 | 
202 |   def set(id: String, tokens: SortedSet[T]): Unit = js.native
203 | 
204 |   def get(id: String): SortedSet[T] = js.native
205 | 
206 |   def has(id: String): Boolean = js.native
207 | 
208 |   def remove(id: String): Unit = js.native
209 | 
210 |   def toJSON(): js.Dynamic = js.native
211 | 
212 |   def load[T](serialisedData: js.Any): Store[T] = js.native
213 | }
214 | 
215 | @js.native
216 | trait ITokenDocument extends js.Object {
217 |   def ref: Double = js.native
218 | 
219 |   def tf: Double = js.native
220 | }
221 | 
222 | @js.native
223 | trait TokenStore extends js.Object {
224 |   def root: js.Dictionary[TokenStore] = js.native
225 | 
226 |   def docs: js.Dictionary[ITokenDocument] = js.native
227 | 
228 |   def length: Double = js.native
229 | 
230 |   def add(token: String, doc: ITokenDocument, root: TokenStore = ???): Unit = js.native
231 | 
232 |   def has(token: String): Boolean = js.native
233 | 
234 |   def getNode(token: String): TokenStore = js.native
235 | 
236 |   def get(token: String, root: TokenStore): js.Dictionary[ITokenDocument] = js.native
237 | 
238 |   def count(token: String, root: TokenStore): Double = js.native
239 | 
240 |   def remove(token: String, ref: String): Unit = js.native
241 | 
242 |   def expand(token: String, memo: js.Array[String] = ???): js.Array[String] = js.native
243 | 
244 |   def toJSON(): js.Dynamic = js.native
245 | 
246 |   def load(serialisedData: js.Any): TokenStore = js.native
247 | }
248 | 
249 | @js.native
250 | trait Lunr extends js.Object {
251 |   def version: String = js.native
252 | 
253 |   def tokenizer(token: String): String = js.native
254 | 
255 |   def stemmer(token: String): String = js.native
256 | 
257 |   def stopWordFilter(token: String): String = js.native
258 | 
259 |   def trimmer(token: String): String = js.native
260 | }
261 | 
262 | @js.native
263 | trait StopWordFilter extends js.Object {
264 |   def stopWords: SortedSet[String] = js.native
265 | }
266 | 
267 | 
268 | object Importedjs {
269 | 
270 |   @js.native
271 |   @JSImport("lunr", JSImport.Namespace)
272 |   def lunr(config: js.Function): Index = js.native
273 | 
274 |   // def version: String = js.native
275 | }
276 | 


--------------------------------------------------------------------------------
/nouislider/src/main/scala/nouislider.scala:
--------------------------------------------------------------------------------
  1 | package scaladget.nouislider
  2 | 
  3 | 
  4 | import org.querki.jsext._
  5 | import scaladget.nouislider.event.SliderEventType
  6 | import com.raquo.laminar.api.L.{*, given}
  7 | import scala.scalajs.js
  8 | import scala.scalajs.js.|
  9 | import js.annotation._
 10 | 
 11 | 
 12 | @js.native
 13 | trait NoUiSliderOptions extends js.Object {
 14 |   val range: js.UndefOr[Range] = js.native
 15 | 
 16 |   val start: js.UndefOr[Double | js.Array[Double]] = js.native
 17 | 
 18 |   val connect: js.UndefOr[Boolean | js.Array[Boolean]] = js.native
 19 | 
 20 |   val limit: js.UndefOr[Double] = js.native
 21 | 
 22 |   val step: js.UndefOr[Double] = js.native
 23 | 
 24 |   val orientation: js.UndefOr[Options.Orientation] = js.native
 25 | 
 26 |   val direction: js.UndefOr[Options.Direction] = js.native
 27 | 
 28 |   val tooltips: js.UndefOr[Boolean | js.Array[Boolean]] = js.native
 29 | }
 30 | 
 31 | 
 32 | object Options extends NoUiSliderOptionsBuilder(noOpts) {
 33 | 
 34 |   type Connect = String
 35 |   val Upper: Connect = "upper"
 36 |   val Lower: Connect = "lower"
 37 | 
 38 |   type Orientation = String
 39 |   val Horizontal = "horizontal"
 40 |   val Vertical = "vertical"
 41 | 
 42 |   type Direction = String
 43 |   val RightToLeft = "rtl"
 44 |   val LeftToRgiht = "ltr"
 45 |   val BottomToTop = "btt"
 46 |   val TopToBottom = "ttb"
 47 | }
 48 | 
 49 | class NoUiSliderOptionsBuilder(val dict: OptMap) extends JSOptionBuilder[NoUiSliderOptions, NoUiSliderOptionsBuilder](new NoUiSliderOptionsBuilder(_)) {
 50 |   def range(v: Range) = jsOpt("range", v)
 51 | 
 52 |   def start(v: Double | js.Array[Double]) = jsOpt("start", v)
 53 | 
 54 |   def connect(c: Options.Connect | js.Array[Boolean]) = jsOpt("connect", c)
 55 | 
 56 |   def limit(c: Double) = jsOpt("limit", c)
 57 | 
 58 |   def step(c: Double) = jsOpt("step", c)
 59 | 
 60 |   def orientation(o: Options.Orientation) = jsOpt("orientation", o)
 61 | 
 62 |   def direction(d: Options.Direction) = jsOpt("direction", d)
 63 | 
 64 |   def tooltips(t: Boolean | js.Array[Boolean]) = jsOpt("tooltips", t)
 65 | 
 66 | }
 67 | 
 68 | @js.native
 69 | trait Range extends js.Object {
 70 |   def min: js.UndefOr[Double] = js.native
 71 | 
 72 |   def max: js.UndefOr[Double] = js.native
 73 | }
 74 | 
 75 | object Range extends RangeBuilder(noOpts)
 76 | 
 77 | class RangeBuilder(val dict: OptMap) extends JSOptionBuilder[Range, RangeBuilder](new RangeBuilder(_)) {
 78 |   def min(v: Double) = jsOpt("min", v)
 79 | 
 80 |   def max(v: Double) = jsOpt("max", v)
 81 | }
 82 | 
 83 | object event {
 84 |   type SliderEventType = String
 85 |   val StartEvent: SliderEventType = "start"
 86 |   val SlideEvent: SliderEventType = "slide"
 87 |   val Dragevent: SliderEventType = "drag"
 88 |   val UpdateEvent: SliderEventType = "update"
 89 |   val ChangeEvent: SliderEventType = "change"
 90 |   val SetEvent: SliderEventType = "set"
 91 |   val EndEvent: SliderEventType = "end"
 92 | }
 93 | 
 94 | @js.native
 95 | @JSImport("nouislider", JSImport.Namespace)
 96 | object noUiSlider extends js.Object {
 97 | 
 98 |   def create(element: org.scalajs.dom.HTMLElement, options: NoUiSliderOptions): Unit = js.native
 99 | }
100 | 
101 | object NoUISliderImplicits {
102 |   implicit class NoUiSliderAPIDom(private val elem: org.scalajs.dom.HTMLElement) {
103 |     def noUiSlider: NoUiSliderAPI = elem.asInstanceOf[js.Dynamic].noUiSlider.asInstanceOf[NoUiSliderAPI]
104 |   }
105 | 
106 |   implicit class NoUiSlideAPILaminar(private val elem: HtmlElement) {
107 |     def noUiSlider: NoUiSliderAPI = elem.ref.asInstanceOf[js.Dynamic].noUiSlider.asInstanceOf[NoUiSliderAPI]
108 |   }
109 | }
110 | @js.native
111 | trait NoUiSliderAPI extends js.Object {
112 |   def set(x: Double | js.Array[Double]): Unit = js.native
113 | 
114 |   def get(): String | js.Array[String] = js.native
115 | 
116 |   // values: Current slider values (array);
117 |   // handle: Handle that caused the event (number);
118 |   // unencoded: Slider values without formatting (array);
119 |   // tap: Event was caused by the user tapping the slider (boolean);
120 |   // positions: Left offset of the handles (array);
121 |   // noUiSlider: slider public Api (noUiSlider);
122 |   def on(event: SliderEventType, callback: js.Function2[Double | js.Array[Double], Double, Unit]): Unit = js.native
123 | 
124 | 
125 | }


--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.10.1
2 | 


--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
 1 | resolvers += Resolver.sonatypeRepo("public")
 2 | 
 3 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
 4 | 
 5 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.3")
 6 | 
 7 | addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1")
 8 | 
 9 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.2")
10 | 
11 | addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0")
12 | 
13 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.11.0")
14 | 
15 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0")
16 | 
17 | addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2")
18 | 


--------------------------------------------------------------------------------
/svg/src/main/scala/scaladget/svg/svg.scala:
--------------------------------------------------------------------------------
 1 | package scaladget.svg
 2 | 
 3 | /*
 4 |  * Copyright (C) 26/03/16 // mathieu.leclaire@openmole.org
 5 |  *
 6 |  * This program is free software: you can redistribute it and/or modify
 7 |  * it under the terms of the GNU Affero General Public License as published by
 8 |  * the Free Software Foundation, either version 3 of the License, or
 9 |  * (at your option) any later version.
10 |  *
11 |  * This program is distributed in the hope that it will be useful,
12 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 |  * GNU Affero General Public License for more details.
15 |  *
16 |  * You should have received a copy of the GNU General Public License
17 |  * along with this program.  If not, see .
18 |  */
19 | 
20 | import org.scalajs.dom
21 | import org.scalajs.dom.raw.Node
22 | import com.raquo.laminar.api.L.svg
23 | import com.raquo.laminar.api.L._
24 | import com.raquo.laminar.api._
25 | import scaladget.tools._
26 | 
27 | object path {
28 |  // implicit def pathToTypedTagPath(p: ): HtmlElement = p
29 | 
30 |   implicit def pathToNode(path: Path): Node = path.render.ref
31 | 
32 |   def start(x: Int, y: Int): Path = Path("").m(x, y)
33 | 
34 |   def apply(st: String = "", precisionPattern: String = "") = Path(
35 |     st,
36 |     precisionPattern
37 |   )
38 | 
39 | 
40 |   type PathOperator = String
41 |   val M: PathOperator = "M"
42 |   val L: PathOperator = "L"
43 |   val H: PathOperator = "H"
44 |   val V: PathOperator = "V"
45 |   val C: PathOperator = "C"
46 |   val Q: PathOperator = "Q"
47 |   val S: PathOperator = "S"
48 |   val T: PathOperator = "T"
49 |   val A: PathOperator = "A"
50 |   val Z: PathOperator = "Z"
51 | 
52 | 
53 |   // presicion: Ex: 1.5f)
54 |   case class Path(svgString: String = "", precisionPattern: String = "") {
55 | 
56 |     def render = svg.path(svg.d := svgString)
57 |       // path()(d := svgString)
58 | 
59 | 
60 |     def expand(value: Double) = {
61 |       if (precisionPattern.isEmpty) value
62 |       else precisionPattern.format(value)
63 |     }
64 | 
65 |     private def append(s: String): Path = copy(svgString = svgString + s" $s")
66 | 
67 |     def m(x: Double, y: Double): Path = append(s"$M ${expand(x)} ${expand(y)}")
68 | 
69 |     def l(x: Double, y: Double): Path = append(s"$L ${expand(x)} ${expand(y)}")
70 | 
71 |     def h(y: Double): Path = append(s"$H ${expand(y)}")
72 | 
73 |     def v(x: Double): Path = append(s"$V ${expand(x)}")
74 | 
75 |     def c(x1: Double, y1: Double, x2: Double, y2: Double, x: Double, y: Double): Path = append(s"$C ${expand(x1)} ${expand(y)}1 ${expand(x1)} ${expand(y)}2 ${expand(x)} ${expand(y)}")
76 | 
77 |     def q(x1: Double, y1: Double, x: Double, y: Double): Path = append(s"$Q ${expand(x1)} ${expand(y1)} ${expand(x)} ${expand(y)}")
78 | 
79 |     def s(x2: Double, y2: Double, x: Double, y: Double): Path = append(s"$S ${expand(x2)} ${expand(y2)} ${expand(x)} ${expand(y)}")
80 | 
81 |     def t(x: Double, y: Double): Path = append(s"$T ${expand(x)} ${expand(y)}")
82 | 
83 |     def a(rx: Double, ry: Double, xAxisRotation: Double, largeArcFlag: Double, sweepFlag: Double, x: Double, y: Double) = append(s"$A ${expand(rx)} ${expand(ry)} ${expand(xAxisRotation)} $largeArcFlag $sweepFlag ${expand(x)} ${expand(y)}")
84 | 
85 |     def z = append("Z")
86 |   }
87 | 
88 | }
89 | 
90 | 


--------------------------------------------------------------------------------
/svgDemo/src/main/resources/css/github-gist.css:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * GitHub Gist Theme
 3 |  * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro
 4 |  */
 5 | 
 6 | .hljs {
 7 |   display: block;
 8 |   background: white;
 9 |   padding: 0.5em;
10 |   color: #333333;
11 |   overflow-x: auto;
12 | }
13 | 
14 | .hljs-comment,
15 | .hljs-meta {
16 |   color: #969896;
17 | }
18 | 
19 | .hljs-string,
20 | .hljs-variable,
21 | .hljs-template-variable,
22 | .hljs-strong,
23 | .hljs-emphasis,
24 | .hljs-quote {
25 |   color: #df5000;
26 | }
27 | 
28 | .hljs-keyword,
29 | .hljs-selector-tag,
30 | .hljs-type {
31 |   color: #a71d5d;
32 | }
33 | 
34 | .hljs-literal,
35 | .hljs-symbol,
36 | .hljs-bullet,
37 | .hljs-attribute {
38 |   color: #0086b3;
39 | }
40 | 
41 | .hljs-section,
42 | .hljs-name {
43 |   color: #63a35c;
44 | }
45 | 
46 | .hljs-tag {
47 |   color: #333333;
48 | }
49 | 
50 | .hljs-title,
51 | .hljs-attr,
52 | .hljs-selector-id,
53 | .hljs-selector-class,
54 | .hljs-selector-attr,
55 | .hljs-selector-pseudo {
56 |   color: #795da3;
57 | }
58 | 
59 | .hljs-addition {
60 |   color: #55a532;
61 |   background-color: #eaffea;
62 | }
63 | 
64 | .hljs-deletion {
65 |   color: #bd2c00;
66 |   background-color: #ffecec;
67 | }
68 | 
69 | .hljs-link {
70 |   text-decoration: underline;
71 | }
72 | 


--------------------------------------------------------------------------------
/svgDemo/src/main/resources/grid.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |     
 5 |     
 6 |     
 7 |     
 8 |     
 9 |     
10 | 
11 | 
12 | 
13 | 
14 | 
15 | 


--------------------------------------------------------------------------------
/svgDemo/src/main/resources/img/svgstar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmole/scaladget/c16d0b784bc5729b4542e25a07a51f1184a64867/svgDemo/src/main/resources/img/svgstar.png


--------------------------------------------------------------------------------
/svgDemo/src/main/resources/svg.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |     
 5 |     
 6 |     
 7 | 
 8 | 
 9 | 
10 | 


--------------------------------------------------------------------------------
/svgDemo/src/main/scala/demo/Demo.scala:
--------------------------------------------------------------------------------
 1 | package demo
 2 | 
 3 | import com.raquo.laminar.api.L._
 4 | 
 5 | /*
 6 |  * Copyright (C) 19/08/16 // mathieu.leclaire@openmole.org
 7 |  *
 8 |  * This program is free software: you can redistribute it and/or modify
 9 |  * it under the terms of the GNU Affero General Public License as published by
10 |  * the Free Software Foundation, either version 3 of the License, or
11 |  * (at your option) any later version.
12 |  *
13 |  * This program is distributed in the hope that it will be useful,
14 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 |  * GNU Affero General Public License for more details.
17 |  *
18 |  * You should have received a copy of the GNU General Public License
19 |  * along with this program.  If not, see .
20 |  */
21 | 
22 | 
23 | trait Demo{
24 |   def elementDemo: ElementDemo
25 | }
26 | 
27 | trait ElementDemo{
28 |   def title: String
29 | 
30 |   def code: String
31 | 
32 |   def cleanCode = {
33 |     if (code.startsWith("{")) code.tail.dropRight(1)
34 |     else code
35 |   }
36 |   def element: SvgElement
37 | 
38 |   def codeWidth: Int = 8
39 | }


--------------------------------------------------------------------------------
/svgDemo/src/main/scala/demo/SVGDemo.scala:
--------------------------------------------------------------------------------
 1 | package demo
 2 | 
 3 | import scaladget.highlightjs.HighlightJS
 4 | import scaladget.bootstrapnative.bsn._
 5 | import com.raquo.laminar.api.L._
 6 | import org.scalajs
 7 | 
 8 | import scala.scalajs.js.annotation._
 9 | 
10 | /*
11 |  * Copyright (C) 24/03/16 // mathieu.leclaire@openmole.org
12 |  *
13 |  * This program is free software: you can redistribute it and/or modify
14 |  * it under the terms of the GNU Affero General Public License as published by
15 |  * the Free Software Foundation, either version 3 of the License, or
16 |  * (at your option) any later version.
17 |  *
18 |  * This program is distributed in the hope that it will be useful,
19 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 |  * GNU Affero General Public License for more details.
22 |  *
23 |  * You should have received a copy of the GNU General Public License
24 |  */
25 | 
26 | object App {
27 | 
28 |   def main(args: Array[String]): Unit = {
29 |     scaladget.highlightjs.scalamode
30 |     HighlightJS.initHighlightingOnLoad()
31 | 
32 |     val demo = SVGStarDemo.elementDemo
33 | 
34 |     val content = div(
35 |       h3(demo.title),
36 |       div(containerFluid,
37 |         div(row, marginLeft := "15", marginTop := "25",
38 |           div(colBS(demo.codeWidth), pre(code(cls := "scala", demo.cleanCode))),
39 |           div(colBS(12 - demo.codeWidth), demo.element)
40 |         )
41 |       )
42 |     )
43 | 
44 |     documentEvents(_.onDomContentLoaded).foreach { _ =>
45 |       render(scalajs.dom.document.body, content)
46 |     }(unsafeWindowOwner)
47 | 
48 |   }
49 | }
50 | 
51 | 


--------------------------------------------------------------------------------
/svgDemo/src/main/scala/demo/SVGStarDemo.scala:
--------------------------------------------------------------------------------
 1 | package demo
 2 | 
 3 | /*
 4 |  * Copyright (C) 22/08/16 // mathieu.leclaire@openmole.org
 5 |  *
 6 |  * This program is free software: you can redistribute it and/or modify
 7 |  * it under the terms of the GNU Affero General Public License as published by
 8 |  * the Free Software Foundation, either version 3 of the License, or
 9 |  * (at your option) any later version.
10 |  *
11 |  * This program is distributed in the hope that it will be useful,
12 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 |  * GNU Affero General Public License for more details.
15 |  *
16 |  * You should have received a copy of the GNU General Public License
17 |  * along with this program.  If not, see .
18 |  */
19 | 
20 | import scaladget.svg.path.Path
21 | import com.raquo.laminar.api.L.svg
22 | import com.raquo.laminar.api.L._
23 | import org.scalajs
24 | 
25 | 
26 | object SVGStarDemo {
27 | 
28 |   val sc = sourcecode.Text {
29 | 
30 |     val star = Path(precisionPattern = "%1.2f").m(150.2235, 20).l(240.22201, 240).l(30.3666, 90).l(270, 90).l(60, 240).z
31 | 
32 |     val scene = svg.g(
33 |       star.render.amend(svg.fill := "red"),
34 |       star.render.amend(svg.fill := "yellow", svg.transform := "scale(0.5) translate(150,130)"),
35 |       star.render.amend(svg.fill := "green", svg.transform := "scale(0.25) translate(450,390)")
36 |     )
37 | 
38 |     svg.svg(
39 |       svg.width := "2500",
40 |       svg.height := "2500",
41 |       scene
42 |     )
43 | 
44 |   }
45 | 
46 |   val elementDemo = new ElementDemo {
47 |     def title: String = "Path"
48 | 
49 |     def code: String = sc.source
50 | 
51 |     def element: SvgElement = sc.value
52 |   }
53 | }
54 | 


--------------------------------------------------------------------------------
/tools/src/main/scala/scaladget/tools/ElementListener.scala:
--------------------------------------------------------------------------------
 1 | package scaladget.tools
 2 | 
 3 | /*
 4 |  * Copyright (C) 14/03/17 // mathieu.leclaire@openmole.org
 5 |  *
 6 |  * This program is free software: you can redistribute it and/or modify
 7 |  * it under the terms of the GNU Affero General Public License as published by
 8 |  * the Free Software Foundation, either version 3 of the License, or
 9 |  * (at your option) any later version.
10 |  *
11 |  * This program is distributed in the hope that it will be useful,
12 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 |  * GNU Affero General Public License for more details.
15 |  *
16 |  * You should have received a copy of the GNU General Public License
17 |  * along with this program.  If not, see .
18 |  */
19 | 
20 | import org.scalajs.dom
21 | import org.scalajs.dom.{Element, Event}
22 | 
23 | object Utils extends utils.Utils
24 | 
25 | package object utils {
26 | 
27 |   trait Utils {
28 | 
29 |     implicit class ElementListener(element: Element) {
30 | 
31 |       def onClickOutside(f: () => Unit) = {
32 |         dom.document.addEventListener("mousedown", (e: Event) => {
33 |           f()
34 |         })
35 |       }
36 |     }
37 | 
38 |     type ID = String
39 | 
40 |     def uuID: ID = scala.util.Random.alphanumeric.take(10).mkString
41 | 
42 |     implicit class ShortID(id: ID) {
43 |       def short: String = id.take(5)
44 | 
45 |       def short(prefix: String): String = s"$prefix$short"
46 |     }
47 | 
48 |   }
49 | 
50 | }


--------------------------------------------------------------------------------
/version.sbt:
--------------------------------------------------------------------------------
1 | ThisBuild / version := "1.12.0-SNAPSHOT"
2 | 


--------------------------------------------------------------------------------