Giter Club home page Giter Club logo

purescript-outwatch's Introduction

Outwatch

The Functional and Reactive Web-Frontend Library for Scala.js

Typelevel incubator codecov Discord outwatch Scala version support

import outwatch._
import outwatch.dsl._
import colibri._
import cats.effect.{IO, IOApp}

object Main extends IOApp.Simple {
  override def run = {
    val counter = Subject.behavior(0)
    val myComponent = div(
      button("+", onClick(counter.map(_ + 1)) --> counter),
      counter,
    )

    Outwatch.renderReplace[IO]("#app", myComponent)
  }
}

In Outwatch, you can describe your whole web application without doing any side effect - you only run your application when rendering it.

  • Write UI-components using pure functions
  • Manage state in a referentially transparent way using cats-effect
  • Built-in lightweight Observable and Subject types from colibri
  • Seamlessly works with existing reactive programming libraries: ZIO, fs2, Airstream, scala.rx
  • Low-boilerplate, many convenient helper functions
  • Built on top of snabbdom, a virtual dom library

You will find interactive examples and explanations in our documentation.

Bugs and Feedback

For bugs, questions and discussions please use GitHub Issues.

Community

We adopted the Scala Code of Conduct. People are expected to follow it when discussing Outwatch on the Github page, Gitter channel, or other venues.

LICENSE

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

purescript-outwatch's People

Contributors

cornerman avatar sectore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purescript-outwatch's Issues

Module not found errors when trying Getting Started with PureScript

OS: macOS 10.12.4
npm: 3.10.10
purescript-outwatch: 0.7.0

Run:

npm install -g purescript pulp bower
pulp init
bower install purescript-outwatch
npm install rxjs snabbdom

Edit src/Main.purs:

module Main where

import OutWatch

main = render "#app" (h1 [text "Hello World"])

Create index.html:

<body>
  <div id="app"></div>
  <script type="text/javascript" src="index.js"></script>
</body>

Run:

pulp --watch browserify --to index.js

Output:

Error 1 of 3:

  in module RxJS.Observable
  at bower_components/purescript-rxps/src/RxJS/Observable.purs line 117, column 1 - line 117, column 52

    Module Test.QuickCheck was not found.
    Make sure the source file exists, and that it has been provided as an input to psc.


  See https://github.com/purescript/documentation/blob/master/errors/ModuleNotFound.md for more information,
  or to contribute content related to this error.

Error 2 of 3:

  in module RxJS.Observable
  at bower_components/purescript-rxps/src/RxJS/Observable.purs line 118, column 1 - line 118, column 37

    Module Test.QuickCheck.Gen was not found.
    Make sure the source file exists, and that it has been provided as an input to psc.


  See https://github.com/purescript/documentation/blob/master/errors/ModuleNotFound.md for more information,
  or to contribute content related to this error.

Error 3 of 3:

  in module Main
  at src/Main.purs line 3, column 1 - line 3, column 16

    Module OutWatch was not found.
    Make sure the source file exists, and that it has been provided as an input to psc.


  See https://github.com/purescript/documentation/blob/master/errors/ModuleNotFound.md for more information,
  or to contribute content related to this error.


* ERROR: Subcommand terminated with exit code 1

alternative monadic API: concrete example with code

based on #7 and #3, I gave a try to some alternative monadic API

๐Ÿ“ operators like ==>, <==, and := are gone
๐Ÿ“ no more [,,,] lists neither

it seems to works well.
the only thing missing seems to be some

instance childReceiverBuilder2 :: ReceiverBuilder ChildStreamReceiverBuilder (Observable (VDomBuilder e)) e  where
instance childrenStreamReceiverBuilder2 :: (Traversable t) => ReceiverBuilder ChildrenStreamReceiverBuilder (Observable (t (VDomBuilder e))) e where

so components can still create dynamically subcomponents with personal handlers for inner state management.

@LukaJCB how does it look ?

module Main where

-- import OutWatch
import Prelude
import OutWatch.Sink as Sink
import RxJS.Observable as Observable
import Builder (class AttributeBuilder, class ReceiverBuilder)
import Control.Alt (map)
import Control.Monad.Aff.Console (CONSOLE)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (log, logShow)
import Control.Monad.RWS.Trans (lift)
import Control.Monad.State (class MonadState, State, StateT(..), execState, execStateT, modify)
import DOM.Node.Types (Element)
import Data.Array (cons, snoc, replicate)
import Data.Array.Partial (head)
import Data.CommutativeRing ((+))
import Data.EuclideanRing ((/))
import Data.Int (round)
import Data.Monoid ((<>))
import Data.Unit (Unit)
import EmitterBuilder (class EmitterBuilder, emitFrom)
import OutWatch (children, createHandler, li, render, src, ul, valueShow, (==>))
import OutWatch (childShow, createNumberHandler, div, img, (:=))
import OutWatch.Attributes (inputNumber, insert, max, text, tpe, (<==))
import OutWatch.Sink (Handler, Observer(..), SinkLike, create)
import OutWatch.Tags (br, hr, input)
import Partial.Unsafe (unsafePartial)
import R.Yolo (x)
import RxJS.Observable (Observable, combineLatest, interval, startWith)
import VDomModifier (VDom)


main :: Eff _ Unit
main = do 
    log "start"
    -- render "#app" app
    (unsafeFirst <$> build test) >>= render "#app"
    log "done"


test2 :: forall e. Number -> VDomBuilder e Unit
test2 max = do
    let def = 0.0
    h <- createHandlerE [def]
    let imageLists = h.src # map (\n -> replicate (round n) (img[src := "img.svg"]))        
    ul_ do
        input_ do
            type_ "range"
            inputNumber_ h
            valueShow_ def
            max_ max
        div_ (children_ imageLists)
        div_ (childShow_ h.src)

test :: forall e. VDomBuilder e Unit
test = ul_ do
    h <- createHandlerE [2.0]
    input_ do
        type_ "number"
        inputNumber_ h
            
    hr_ (pure unit)
    li_ do
        text_ "the cat is  "
        text_ "nice"
    li_ (text_ "yay")
    li_ do 
        text_ "sub elements"
        ul_ do
            li_ (text_ "ok")
            li_ (text_ "ok")
            li_ (test2 1.0)
            li_ (test2 3.0)
        
----------------------------------------------------------------------------
---- types -----------------------------------------------------------------

foreign import data H :: !
type VDomBuilder e a = StateT (Array (VDom e)) (Eff (h::H|e)) a
type NodeBuilder e = VDomBuilder e Unit -> VDomBuilder e Unit

----------------------------------------------------------------------------
-- Handlers, Observables, Sinks --------------------------------------------

createHandlerE :: forall a e. Array a -> VDomBuilder e (Handler e a)
createHandlerE a = lift $ pure (createHandler a)

----------------------------------------------------------------------------
-- Elements ----------------------------------------------------------------

wrapElem :: forall e. (Array (VDom e) -> (VDom e)) -> VDomBuilder e Unit -> VDomBuilder e Unit
wrapElem e b = lift (e <$> execStateT b []) >>= push 

ul_ :: forall e. NodeBuilder e
ul_ = wrapElem ul

li_ :: forall e. NodeBuilder e
li_ = wrapElem li

div_ :: forall e. NodeBuilder e
div_ = wrapElem div

br_ = wrapElem br
hr_ = wrapElem hr

input_ :: forall e. NodeBuilder e
input_ = wrapElem input

text_ :: forall e. String -> VDomBuilder e Unit
text_ t =  push (text t)

-- ul_1 :: forall e. VDomBuilder e Unit -> VDomBuilder e Unit
-- ul_1 b = lift (ul <$> execStateT b []) >>= push

----------------------------------------------------------------------------
-- Attributes --------------------------------------------------------------

wrapAttribyte :: 
    forall builder value e. 
    AttributeBuilder builder value =>  
    builder ->
    value ->
    VDomBuilder e Unit
wrapAttribyte b v = push (b := v)

type_ :: forall e. String -> VDomBuilder e Unit
type_ = wrapAttribyte tpe

valueShow_ :: forall s e. Show s => s -> VDomBuilder e Unit
valueShow_ = wrapAttribyte valueShow

max_ :: forall e. Number -> VDomBuilder e Unit
max_ = wrapAttribyte max

----------------------------------------------------------------------------
-- Emitter -----------------------------------------------------------------

wrapEmitter :: forall builder a e r.       
  EmitterBuilder builder a e => 
  builder -> 
  SinkLike e a r ->
  VDomBuilder e Unit
wrapEmitter b s = push (b ==> s)

-- inputNumber_ :: forall e. Number -> VDomBuilder e Unit
inputNumber_ = wrapEmitter inputNumber

----------------------------------------------------------------------------
-- Receiver ----------------------------------------------------------------

wrapReceiver :: forall builder stream e.
  ReceiverBuilder builder stream e => 
  builder -> 
  stream ->
  VDomBuilder e Unit
wrapReceiver b s = push (b <== s)

children_ :: forall e. Observable (Array (VDom e)) -> VDomBuilder e Unit
children_ s = push (children <== s)

-- instance childReceiverBuilder :: ReceiverBuilder ChildStreamReceiverBuilder (Observable (VDom e)) e  where
--   bindFrom builder obs =
--     let valueStream = map modifierToVNode obs
--     in Receiver (ChildStreamReceiver valueStream)

-- children_2 :: forall e. Observable (VDomBuilder e Unit) -> VDomBuilder e Unit
-- children_2 = do 
--     children

childShow_ :: forall a e. Show a => Observable a -> VDomBuilder e Unit
childShow_ = wrapReceiver childShow
-- ReceiverBuilder builder stream eff | stream -> eff, builder -> stream where
--   bindFrom :: builder -> stream -> VDom eff

----------------------------------------------------------------------------
-- Helpers -----------------------------------------------------------------

push :: forall vdom m. (MonadState (Array vdom) m) => vdom -> m Unit
push = (\e l -> snoc l e) >>> modify

unsafeFirst :: forall a. Array a -> a 
unsafeFirst a = unsafePartial (head a)

build :: forall e a. VDomBuilder e a -> Eff (h::H|e) (Array (VDom e))
build b = execStateT b []

        
------------------------------------------------------------------------------
---- old ---------------------------------------------------------------------


app :: forall e. VDom ( console :: CONSOLE | e )
app = div 
    [ root 1.0
    , root 12.0
    , root 100.0
    ]

root :: forall e. Number -> VDom (console :: CONSOLE |e)
root a = ul
    [ input
        [ tpe := "range"
        , inputNumber ==> sliderEvents
        , valueShow := 0
        , max := a
        ]
    , div [children <== imageLists]
    , div [childShow <== sliderEvents.src]
    , div [childShow <== sliderEvents.src]
    , div [childShow <== sliderEvents.src]
    ]
    where
        imageLists = sliderEvents.src
            # map round
            # map (\n -> replicate n (img[src := "img.svg"]))

sliderEvents :: forall e. Handler e Number
sliderEvents = createHandler [0.0]

Issues building a project with PS 0.11.4

When trying to build one of the examples, I'm running into the following issues, which seem related to the purescript 0.11 transition:

Using pulp 11.0.0, purescript 0.11.4, if I do:

git clone https://github.com/OutWatch/purescript-outwatch
cd purescript-outwatch
npm install
bower install
npm run build:example-counter

I get:


  The following values are not defined in the foreign module for module RxJS.BehaviorSubject:

    _empty

* ERROR: Subcommand terminated with exit code 1

npm ERR! Darwin 16.5.0
npm ERR! argv "/usr/local/Cellar/node/7.2.1/bin/node" "/usr/local/bin/npm" "run" "build:example-counter-store"
npm ERR! node v7.2.1
npm ERR! npm  v4.0.3
npm ERR! code ELIFECYCLE
npm ERR! [email protected] build:example-counter-store: `npm run clean:output && pulp browserify --optimise --include examples/counter-store/src --to examples/counter-store/dist/app.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] build:example-counter-store script 'npm run clean:output && pulp browserify --optimise --include examples/counter-store/src --to examples/counter-store/dist/app.js'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the purescript-outwatch package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     npm run clean:output && pulp browserify --optimise --include examples/counter-store/src --to examples/counter-store/dist/app.js
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs purescript-outwatch
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls purescript-outwatch
npm ERR! There is likely additional logging output above.

If I try to set up a fresh project with:

pulp init
bower install purescript-outwatch

I get a long list of dialogs, this being the first:

Unable to find a suitable version for purescript-prelude, please choose one by typing one of the numbers below:
    1) purescript-prelude#^2.1.0 which resolved to 2.5.0 and is required by purescript-control#2.0.0, purescript-dom#3.8.1, purescript-eff#2.0.0, purescript-functions#2.0.0, purescript-invariant#2.0.0, purescript-newtype#1.3.0, purescript-outwatch#0.6.1
    2) purescript-prelude#^2.5.0 which resolved to 2.5.0 and is required by purescript-either#2.2.1, purescript-maybe#2.1.1
    3) purescript-prelude#^2.3.0 which resolved to 2.5.0 and is required by purescript-snabbdom#0.2.3
    4) purescript-prelude#^2.0.0 which resolved to 2.5.0 and is required by purescript-form-urlencoded#2.0.0
    5) purescript-prelude#^2.4.0 which resolved to 2.5.0 and is required by purescript-tuples#3.2.0
    6) purescript-prelude#^3.0.0 which resolved to 3.0.0 and is required by outw2, purescript-control#3.0.0, purescript-dom#4.3.1, purescript-eff#3.1.0, purescript-either#3.0.0, purescript-functions#3.0.0, purescript-invariant#3.0.0, purescript-maybe#3.0.0, purescript-newtype#2.0.0, purescript-nonempty#4.0.0, purescript-rxjs#eaf9609267, purescript-tuples#4.0.0

If I'm answering all with the highest option (6 in the above case) - which should give me PS 11 compatibility(?), and then run:

pulp build

I get:

Error 1 of 5:

  at bower_components/purescript-affjax/src/Network/HTTP/Affjax.purs line 58, column 1 - line 58, column 1

    Unable to parse module:
    The `!` symbol is no longer used for the kind of effects.
  The new equivalent is the named kind `Effect`, defined in `Control.Monad.Eff` in the `purescript-eff` library.


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsingModule.md for more information,
  or to contribute content related to this error.

Error 2 of 5:

  at bower_components/purescript-affjax/src/Network/HTTP/Affjax/Request.purs line 30, column 1 - line 30, column 1

    Unable to parse module:
    The `*` symbol is no longer used for the kind of types.
  The new equivalent is the named kind `Type`.


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsingModule.md for more information,
  or to contribute content related to this error.

plus 3 more errors related to PS 0.11 issues.

I eventually could get an example built by setting the critical libs as direct dependencies:

  "dependencies": {
    "purescript-prelude": "^3.0.0",
    "purescript-console": "^3.0.0",
    "purescript-eff": "^3.1.0",
    "purescript-snabbdom": "^1.0.0",
    "purescript-affjax": "^4.0.0",
    "purescript-foldable-traversable": "^3.0.0",
    "purescript-argonaut-core": "^3.1.0",
    "purescript-outwatch": "^0.6.1",
    "purescript-quickcheck": "^4.0.0",
    "purescript-rxjs": "https://github.com/LukaJCB/purescript-rxjs.git"

pulp browserify --to index.js then succeeds, however when loading the bundle in the bowser I get a multitude of errors like:

Uncaught TypeError: Cannot read property 'id' of undefined
    at emptyNodeAt (index.js:18128)
    at patch (index.js:18367)
    at index.js:41565
    at index.js:19510
    at index.js:19510
    at index.js:19510
    at SafeSubscriber._next (index.js:40681)
    at SafeSubscriber.__tryOrSetError (index.js:1284)
    at SafeSubscriber.next (index.js:1226)
    at Subscriber._next (index.js:1166)

provide more helpers like inputRange

Do you plan to add helpers like

inputRange :: forall e. Array (VDom e) -> VDom e
inputRange opts = input (cons (tpe := "number") opts)

?

If not, would you accept a PR with some of those ?

createNumberHandler, createSink, etc should be Effectfull functions

From what I understand, as of now, it's only working because of purescript lack of optimisation.
both inlining and caching should break the code.

Am I missing something ?
edit: Is there something related to strictness I'm not understanding here ?

should it be something along the lines of:

createHandler :: forall a e. Array a -> Eff (rx :: RX) (Handler e a) 

Any plans for OutWatch for ReasonML?

Sorry for this totally offtopic question. I was just super curious. You already did it in two languages, which is awesome. Any plans for ReasonML?

about naming, consistency, name clashes, module organisation

I love OutWatch's overall design, but have some issues with consistency, modules organisation, and naming clashes (I saw the latests re-organisation commits, but I think there is more to discuss)

Purescript

names like div are really annoying to use because they clash with prelude.
name like tpe are also annoying to use because they are harder to remember

would it make sense to follow lucid design and prefix or postfix everything with an _ ?

  • no more Names that conflict with base (div, id, head, map) -> only: div_; ul_, id_, etc.
  • no more names that are keywords, (type -> tpe or type_ or ..., class -> cls or class or ...) -> only class_, type_, etc.

it could also help adding back an global OutWatch that reexport most useful functions

(to see some more lucid examples, github link is here: https://github.com/chrisdone/lucid)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.