├── nim.cfg
├── UnitTesting
├── .npmrc
├── README.md
├── jasmine.nim
├── test.nim
├── package.json
└── karma.conf.js
├── .gitignore
├── .gifs
├── demo_elmlike1.gif
├── demo_elmlike2.gif
└── demo_transitiongroups.gif
├── JsIntegration
├── README.md
├── demo.js
├── demo.nim
└── demo.html
├── VueLike1
├── README.md
├── demo.html
├── counter_component.nim
└── demo.nim
├── ElmLike1
├── README.md
├── demo.html
└── demo.nim
├── ElmLike2
├── README.md
├── demo.html
└── demo.nim
├── Refs
├── demo.html
└── demo.nim
├── demo.css
├── DemoTransitionGroups
├── demo.html
├── README.md
└── demo.nim
├── README.md
├── karax_utils.nim
└── .travis.yml
/nim.cfg:
--------------------------------------------------------------------------------
1 | --path: "../karax/src"
--------------------------------------------------------------------------------
/UnitTesting/.npmrc:
--------------------------------------------------------------------------------
1 | loglevel=silent
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | nimcache
2 | debug.nim
3 | node_modules
4 | /UnitTesting/test.js
--------------------------------------------------------------------------------
/.gifs/demo_elmlike1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluenote10/KaraxExamples/HEAD/.gifs/demo_elmlike1.gif
--------------------------------------------------------------------------------
/.gifs/demo_elmlike2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluenote10/KaraxExamples/HEAD/.gifs/demo_elmlike2.gif
--------------------------------------------------------------------------------
/.gifs/demo_transitiongroups.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluenote10/KaraxExamples/HEAD/.gifs/demo_transitiongroups.gif
--------------------------------------------------------------------------------
/JsIntegration/README.md:
--------------------------------------------------------------------------------
1 | ## JS Integration
2 |
3 | Just a quick experiment how to embed existing JS code at compile time.
4 |
--------------------------------------------------------------------------------
/JsIntegration/demo.js:
--------------------------------------------------------------------------------
1 |
2 | function jsHelloWorld(x) {
3 | console.log("Running jsHelloWorld");
4 | console.log(x);
5 | return 42;
6 | }
--------------------------------------------------------------------------------
/VueLike1/README.md:
--------------------------------------------------------------------------------
1 | ## Vue-like 1
2 |
3 | Example showing functionality similar to Vue components using a "props down, emit up" architecture.
4 |
5 |
--------------------------------------------------------------------------------
/ElmLike1/README.md:
--------------------------------------------------------------------------------
1 | ## Elm-like 1
2 |
3 | Example going in the direction of the Elm architecture.
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/JsIntegration/demo.nim:
--------------------------------------------------------------------------------
1 |
2 | # Embed JS code at compile time
3 | const s = staticRead("demo.js")
4 | {.emit: s.}
5 |
6 | # Write corresponding function signatures
7 | proc jsHelloWorld(x: int): int {.importc, nodecl.}
8 |
9 | discard jsHelloWorld(1)
10 |
--------------------------------------------------------------------------------
/ElmLike2/README.md:
--------------------------------------------------------------------------------
1 | ## Elm-like 2
2 |
3 | Example going in the direction of the Elm architecture.
4 |
5 | Instead of currying this example uses local message handlers,
6 | which send a message to a global update function.
7 |
8 | 
9 |
--------------------------------------------------------------------------------
/Refs/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Elm-like 1
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/VueLike1/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | VueLike
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ElmLike1/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Elm-like 1
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ElmLike2/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Elm-like 2
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | width: 800px;
4 | margin: 20px auto;
5 | padding: 20px;
6 | border: 1px solid #EEE;
7 | }
8 |
9 | .button {
10 | margin: 5px;
11 | }
12 |
13 | .word {
14 | margin: 5px;
15 | padding: 5px;
16 | border: 1px solid #EEE;
17 | display: inline-block;
18 | }
19 |
20 | .animate-on-transform {
21 | transition: transform 1s;
22 | }
--------------------------------------------------------------------------------
/JsIntegration/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo Transition Groups
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/DemoTransitionGroups/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo Transition Groups
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Karax Examples
2 |
3 | [](https://travis-ci.org/bluenote10/KaraxExamples)
4 |
5 | Some Karax example projects:
6 |
7 | * [Elm-like 1](ElmLike1)
8 | * [Elm-like 2](ElmLike2)
9 | * [Transition Groups](DemoTransitionGroups)
10 | * [Js Integration](JsIntegration)
11 | * [Unit Testing](UnitTesting)
12 |
--------------------------------------------------------------------------------
/UnitTesting/README.md:
--------------------------------------------------------------------------------
1 | ## Unit Testing
2 |
3 | Experiment to get headless-browser testing to work.
4 |
5 | ### Usage
6 |
7 | Install dependencies:
8 |
9 | $ npm install
10 |
11 | To run Karma tests:
12 |
13 | $ npm test
14 |
15 | This will internally run the npm scripts `test:build` (which runs the Nim compiler)
16 | and `test:karma` (which runs Karma on the compiler output).
17 |
18 | For a continous watch + build + test cycle run:
19 |
20 | $ npm run test:watch
21 |
--------------------------------------------------------------------------------
/DemoTransitionGroups/README.md:
--------------------------------------------------------------------------------
1 | ## Transitions Groups
2 |
3 | Small example of how to implement transition groups in Karax.
4 |
5 | 
6 |
7 | Currently requires a modified version of Karax, see my PR's for Karax.
8 |
9 | Inspired by:
10 |
11 | * Paul Lewis's [FLIP transitions](https://aerotwist.com/blog/flip-your-animations/)
12 | * Vue's [list move transitions](https://vuejs.org/v2/guide/transitions.html#List-Move-Transitions)
13 |
--------------------------------------------------------------------------------
/UnitTesting/jasmine.nim:
--------------------------------------------------------------------------------
1 | import future
2 |
3 | proc describe*(description: cstring, body: () -> void) {.importc.}
4 |
5 | proc it*(description: cstring, body: () -> void) {.importc.}
6 |
7 | type
8 | JasmineRequireObj* {.importc.} = ref object
9 | `not`* {.importc: "not".}: JasmineRequireObj
10 |
11 | proc expect*[T](x: T): JasmineRequireObj {.importc.}
12 |
13 | proc toBe*[T](e: JasmineRequireObj, x: T) {.importcpp.}
14 |
15 | #proc `not`*(e: JasmineRequireObj): JasmineRequireObj {.importc.}
16 |
17 |
--------------------------------------------------------------------------------
/UnitTesting/test.nim:
--------------------------------------------------------------------------------
1 |
2 | import dom, vdom, times, karax, karaxdsl, jdict, jstrutils, parseutils, sequtils
3 | import future
4 | import jasmine
5 |
6 | static:
7 | echo: "Compiling..."
8 |
9 | # To clear console manually you can use:
10 | # {.emit: "console.log('\u001b[2J\u001b[0;0H');".}
11 |
12 | describe("A test suite"):
13 |
14 | it("should work"):
15 | var a = 1
16 | expect(true).toBe(true)
17 | expect(a).toBe(1)
18 | expect(a).`not`.toBe(2)
19 |
20 | let name = "asdf"
21 |
22 | it(name):
23 | discard
24 |
--------------------------------------------------------------------------------
/UnitTesting/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "karax-unit-test-tests",
3 | "version": "0.1.0",
4 | "description": "Testing unit tests",
5 | "scripts": {
6 | "test": "npm run test:build --silent && npm run test:karma --silent",
7 | "test:build": "nim js test.nim",
8 | "test:karma": "karma start karma.conf.js --singleRun",
9 | "test:watch": "watch 'npm run test' . --interval=0.1"
10 | },
11 | "devDependencies": {
12 | "jasmine": "^2.6.0",
13 | "karma": "^1.7.0",
14 | "karma-jasmine": "^1.1.0",
15 | "karma-phantomjs-launcher": "^1.0.4",
16 | "karma-spec-reporter": "0.0.31",
17 | "watch": "^1.0.2"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/VueLike1/counter_component.nim:
--------------------------------------------------------------------------------
1 | include karaxprelude
2 | import karaxdsl
3 | import future
4 |
5 |
6 | type
7 | CounterComponent* = ref object
8 | # holds internal state like `data` in Vue
9 | counter*: int
10 | emitInc*: () -> void
11 | emitDec*: () -> void
12 |
13 |
14 | proc render*(self: CounterComponent): VNode =
15 |
16 | proc onInc(ev: Event, n: VNode) =
17 | self.counter += 1
18 | self.emitInc()
19 |
20 | proc onDec(ev: Event, n: VNode) =
21 | self.counter -= 1
22 | self.emitDec()
23 |
24 | result = buildHtml():
25 | tdiv():
26 | tdiv():
27 | text "Counter value: " & $self.counter
28 | button(onclick=onInc):
29 | text "inc"
30 | button(onclick=onDec):
31 | text "dec"
32 |
--------------------------------------------------------------------------------
/ElmLike1/demo.nim:
--------------------------------------------------------------------------------
1 |
2 | include karaxprelude
3 | import jstrutils, jdict, kdom
4 | import ../karax_utils
5 |
6 | type
7 | # Elm's `model`
8 | Model = ref object
9 | toggle: bool
10 |
11 |
12 | proc init(): Model =
13 | # Elm's `init`
14 | Model(toggle: true)
15 |
16 |
17 | proc onClick(model: Model, ev: Event, n: VNode) =
18 | # Elm's `update`. Note that we could have
19 | # a global `update` like Elm as well. In this case
20 | # the event handlers would construct a message type
21 | # and pass it to a model update function.
22 | # See second ElmLike demo for an example
23 | kout(ev)
24 | kout(model)
25 | model.toggle = model.toggle xor true
26 |
27 |
28 | proc view(model: Model): VNode =
29 | # Elm's `view`.
30 | result = buildHtml():
31 | tdiv:
32 | # message handler close over the model using the curry macro
33 | button(onclick=curry(onClick, model)):
34 | text "click me"
35 | tdiv:
36 | text "Toggle state:"
37 | tdiv:
38 | if model.toggle:
39 | text "true"
40 | else:
41 | text "false"
42 |
43 | # Putting it all together
44 | proc runMain() =
45 | var model = init()
46 |
47 | proc renderer(): VNode =
48 | view(model)
49 |
50 | setRenderer renderer
51 |
52 | runMain()
53 |
--------------------------------------------------------------------------------
/VueLike1/demo.nim:
--------------------------------------------------------------------------------
1 | include karaxprelude
2 | import karaxdsl, ../karax_utils
3 | import future, sequtils
4 |
5 | import counter_component
6 |
7 | type
8 | MainComponent = ref object
9 | # holds internal state like `data` in Vue
10 | children: seq[CounterComponent]
11 | sumOfCounters: int
12 |
13 | proc render(self: MainComponent): VNode =
14 | result = buildHtml():
15 | tdiv():
16 | for child in self.children:
17 | child.render
18 | tdiv():
19 | text "Sum of counters: " & $self.sumOfCounters
20 |
21 |
22 | proc registerCounterComponent(self: MainComponent, counterInit: int) =
23 |
24 | proc onInc() =
25 | self.sumOfCounters += 1
26 |
27 | proc onDec() =
28 | self.sumOfCounters -= 1
29 |
30 | self.children.add(CounterComponent(
31 | counter: counterInit,
32 | emitInc: onInc,
33 | emitDec: onDec,
34 | ))
35 |
36 |
37 | proc runMain() =
38 |
39 | let initValues = @[2, 5, 3]
40 | let initSumOfCounters = initValues.reduce((a, b) => a + b)
41 | var mainComponent = MainComponent(
42 | sumOfCounters: initSumOfCounters,
43 | children: @[],
44 | )
45 |
46 | for counterInit in initValues:
47 | mainComponent.registerCounterComponent(counterInit)
48 |
49 | proc renderer(): VNode =
50 | mainComponent.render()
51 |
52 | setRenderer renderer
53 |
54 | runMain()
55 |
--------------------------------------------------------------------------------
/ElmLike2/demo.nim:
--------------------------------------------------------------------------------
1 |
2 | include karaxprelude
3 | import jstrutils, jdict, kdom
4 | import ../karax_utils
5 | import future
6 |
7 | type
8 | # Elm's `model`
9 | Model = ref object
10 | items: seq[int] not nil
11 | counter: int
12 |
13 | Message = enum
14 | AddItem,
15 | RemoveItem
16 |
17 |
18 | proc init(): Model =
19 | # Elm's `init`
20 | Model(
21 | items: @[],
22 | counter: 1,
23 | )
24 |
25 |
26 | proc update(model: Model, msg: Message) =
27 | # Elm's `update` (but in-place)
28 | case msg
29 | of AddItem:
30 | let toAdd = model.counter
31 | model.counter += 1
32 | model.items.add(toAdd)
33 | of RemoveItem:
34 | if model.items.len > 0:
35 | model.items.setLen(model.items.len - 1)
36 |
37 |
38 | proc view(model: Model): VNode =
39 | # Elm's `view`.
40 |
41 | proc onAdd(ev: Event, n: VNode) =
42 | update(model, Message.AddItem)
43 |
44 | proc onRemove(ev: Event, n: VNode) =
45 | update(model, Message.RemoveItem)
46 |
47 | result = buildHtml():
48 | tdiv:
49 | button(onclick=onAdd):
50 | text "Add item"
51 | button(onclick=onRemove):
52 | text "Remove item"
53 | tdiv:
54 | for item in model.items:
55 | tdiv:
56 | text "Item: " & $item
57 |
58 | # Alternatively, the message handler could be written without
59 | # explicitly creating procs, i.e.:
60 | # button(onclick=(ev: Event, n: VNode) => update(model, Message.AddItem))
61 | # button(onclick=(ev: Event, n: VNode) => update(model, Message.RemoveItem))
62 |
63 |
64 | # Putting it all together
65 | proc runMain() =
66 | var model = init()
67 |
68 | proc renderer(): VNode =
69 | view(model)
70 |
71 | setRenderer renderer
72 |
73 | runMain()
74 |
--------------------------------------------------------------------------------
/karax_utils.nim:
--------------------------------------------------------------------------------
1 | import macros
2 | import future
3 |
4 |
5 | macro curry*(f: typed, args: varargs[untyped]): untyped = # ,
6 |
7 | # echo f.treeRepr
8 | # echo args.treeRepr
9 |
10 | # echo f.getType.treeRepr
11 | # echo f.getTypeInst.treeRepr
12 | # echo f.getTypeImpl.treeRepr
13 |
14 | var tTypeImpl = f.getTypeImpl
15 | # echo tTypeImpl.len
16 | # echo tTypeImpl.kind
17 | # echo tTypeImpl.typeKind
18 | # echo tTypeImpl.treeRepr
19 |
20 | if tTypeImpl.typeKind != ntyProc:
21 | error "curry requires a proc as its first argument, but received: " & f.repr &
22 | " which has typeKind " & $tTypeImpl.typeKind
23 |
24 | # we need the FormalParams which are the first child
25 | let formalParams = tTypeImpl[0]
26 |
27 | # and we have to iterate its childred starting at 1
28 | # because child 0 is the return type
29 | let returnType = formalParams[0]
30 | # echo returnType.treeRepr
31 |
32 | var params = @[
33 | returnType,
34 | ]
35 |
36 | var call = newCall(f)
37 | for arg in args:
38 | call.add(arg)
39 |
40 | for i in (1 + args.len) ..< formalParams.len:
41 | let child = formalParams[i]
42 | # echo child.treeRepr
43 | if child.kind != nnkIdentDefs:
44 | error "Function parameters are expected to be IdentDefs, but received: " & child.repr &
45 | " which has typeKind " & $child.kind
46 | else:
47 | let nameIdent = newIdentNode($child[0])
48 | let typeIdent = newIdentNode($child[1])
49 | params.add(newIdentDefs(name = nameIdent, kind = typeIdent))
50 | call.add(newIdentNode($child[0]))
51 |
52 | let body = newStmtList(call)
53 |
54 | result = newProc(params=params, body=body, procType=nnkLambda)
55 | # echo result.treeRepr
56 |
57 |
58 | proc reduce*[T](s: seq[T], f: (T, T) -> T): T =
59 | result = T(0)
60 | for x in s:
61 | result = f(result, x)
62 |
--------------------------------------------------------------------------------
/UnitTesting/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 |
4 | // base path that will be used to resolve all patterns (eg. files, exclude)
5 | basePath: '',
6 |
7 | // frameworks to use
8 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
9 | frameworks: ['jasmine'],
10 |
11 | // list of files / patterns to load in the browser
12 | files: [
13 | // the main test file is served by Karma
14 | 'nimcache/test.js'
15 | ],
16 |
17 | // list of files to exclude
18 | exclude: [
19 | ],
20 |
21 | // preprocess matching files before serving them to the browser
22 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
23 | preprocessors: {
24 | },
25 |
26 | // test results reporter to use
27 | // possible values: 'dots', 'progress'
28 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
29 | reporters: ['spec'],
30 |
31 | // web server port
32 | port: 9876,
33 |
34 | // enable / disable colors in the output (reporters and logs)
35 | colors: true,
36 |
37 | // level of logging
38 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
39 | logLevel: config.LOG_INFO,
40 |
41 | // enable / disable watching file and executing tests whenever any file changes
42 | autoWatch: true,
43 |
44 | // start these browsers
45 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
46 | browsers: ['PhantomJS'], // 'Firefox'
47 |
48 | // Continuous Integration mode
49 | // if true, Karma captures browsers, runs the tests and exits
50 | singleRun: false,
51 |
52 | // Concurrency level
53 | // how many browser should be started simultaneous
54 | concurrency: Infinity
55 | })
56 | }
57 |
--------------------------------------------------------------------------------
/Refs/demo.nim:
--------------------------------------------------------------------------------
1 |
2 | include karaxprelude
3 | import jstrutils, jdict, kdom
4 | import ../karax_utils
5 |
6 | type
7 | Model = ref object
8 | counter: int
9 | textA: cstring
10 | textB: cstring
11 | messages: seq[cstring]
12 |
13 |
14 | var vnodeMap = newJDict[cstring, (VNode, VNode)]()
15 |
16 |
17 | proc registerAs(n: VNode, name: cstring): VNode =
18 | if name in vnodeMap:
19 | # store new candidate node
20 | vnodeMap[name] = (vnodeMap[name][0], n)
21 | else:
22 | vnodeMap[name] = (n, n)
23 | result = n
24 |
25 |
26 | proc onClick(model: Model, ev: Event, n: VNode) =
27 | model.counter += 1
28 |
29 | if model.counter mod 5 == 0:
30 | swap(model.textA, model.textB)
31 |
32 | let idsToCheck = [cstring"textA", cstring"textB"]
33 | for id in idsToCheck:
34 | let (vnodeOld, vnodeNew) = vnodeMap[id]
35 | var vnode: VNode
36 | # If the new VNode candidate has a DOM we can store it
37 | if not vnodeNew.dom.isNil:
38 | vnodeMap[id] = (vnodeNew, vnodeNew)
39 | vnode = vnodeNew
40 | # Otherwise the old VNode must still be valid
41 | else:
42 | vnode = vnodeOld
43 | let node = vnode.dom
44 | if not node.isNil:
45 | let bb = node.getBoundingClientRect()
46 | model.messages.add(
47 | cstring"Element: " & id & " has width " & $bb.width
48 | )
49 |
50 |
51 | proc wordSpan(spanText: cstring): VNode =
52 | result = buildHtml():
53 | span(class="word"):
54 | text spanText
55 |
56 | proc view(model: Model): VNode =
57 |
58 | let spanA = wordSpan(model.textA).registerAs("textA")
59 | let spanB = wordSpan(model.textB).registerAs("textB")
60 |
61 | result = buildHtml():
62 | tdiv:
63 | button(onclick=curry(onClick, model)):
64 | text "click me"
65 | spanA
66 | spanB
67 | for message in model.messages:
68 | tdiv:
69 | text message
70 |
71 |
72 | proc runMain() =
73 | var model = Model(
74 | counter: 1,
75 | textA: cstring"Text A",
76 | textB: cstring"Text B (longer than text A)",
77 | messages: @[]
78 | )
79 |
80 | proc renderer(): VNode =
81 | view(model)
82 |
83 | setRenderer renderer
84 |
85 | runMain()
86 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Copied from https://github.com/nim-lang/Nim/wiki/TravisCI
2 | language: node_js
3 | env:
4 | # Build and test against the master and devel branches of Nim
5 | - BRANCH=master
6 | - BRANCH=devel
7 | matrix:
8 | allow_failures:
9 | # Ignore failures when building against the devel Nim branch
10 | - env: BRANCH=devel
11 | fast_finish: true
12 | install:
13 | - |
14 | if [ ! -x nim-$BRANCH/bin/nim ]; then
15 | git clone -b $BRANCH --depth 1 git://github.com/nim-lang/nim nim-$BRANCH/
16 | cd nim-$BRANCH
17 | git clone --depth 1 git://github.com/nim-lang/csources csources/
18 | cd csources
19 | sh build.sh
20 | cd ..
21 | rm -rf csources
22 | bin/nim c koch
23 | ./koch boot -d:release
24 | ./koch nimble
25 | else
26 | cd nim-$BRANCH
27 | git fetch origin
28 | if ! git merge FETCH_HEAD | grep "Already up-to-date"; then
29 | bin/nim c koch
30 | ./koch boot -d:release
31 | ./koch nimble
32 | fi
33 | fi
34 | cd ..
35 | before_script:
36 | - |
37 | echo "Running with Nim from: nim-$BRANCH"
38 | ls -l "nim-$BRANCH/bin"
39 | NIMABSPATH=`readlink -f nim-$BRANCH/bin`
40 | export PATH="${NIMABSPATH}${PATH:+:$PATH}"
41 | echo "Path: $PATH"
42 | cd "$TRAVIS_BUILD_DIR/.."
43 | git clone git://github.com/pragmagic/karax.git
44 | cd karax
45 | nimble install
46 | script:
47 | - |
48 | cd "$TRAVIS_BUILD_DIR/ElmLike1"
49 | nim js demo.nim
50 | - |
51 | cd "$TRAVIS_BUILD_DIR/ElmLike2"
52 | nim js demo.nim
53 | - |
54 | cd "$TRAVIS_BUILD_DIR/VueLike1"
55 | nim js demo.nim
56 | - |
57 | cd "$TRAVIS_BUILD_DIR/DemoTransitionGroups"
58 | nim js demo.nim
59 | echo "Ignoring for now until PR is merged"
60 | - |
61 | cd "$TRAVIS_BUILD_DIR/JsIntegration"
62 | nim js demo.nim
63 | - |
64 | cd "$TRAVIS_BUILD_DIR/UnitTesting"
65 | npm install
66 | npm test
67 | # Optional: build docs.
68 | # - nim doc --docSeeSrcUrl:https://github.com/AUTHOR/MYPROJECT/blob/master --project MYFILE.nim
69 | cache:
70 | directories:
71 | - nim-master
72 | - nim-devel
73 | notifications:
74 | email: false
--------------------------------------------------------------------------------
/DemoTransitionGroups/demo.nim:
--------------------------------------------------------------------------------
1 |
2 | include karaxprelude
3 | import jstrutils, jdict, dom
4 | import future, sequtils, random
5 | import ../karax_utils
6 |
7 | type
8 | IdentifyableString = object
9 | s: string
10 | id: int
11 |
12 | Position = object
13 | x: float
14 | y: float
15 |
16 | Model = ref object
17 | text: seq[IdentifyableString]
18 |
19 | positions: JDict[string, Position]
20 |
21 |
22 | proc onClick(model: Model, ev: Event, n: VNode) =
23 |
24 | for word in model.text:
25 | let id = "word-" & $word.id
26 | let element = getElementById(id)
27 | if not element.isNil:
28 | let boundingBox = element.getBoundingClientRect()
29 | model.positions[id] = Position(
30 | x: boundingBox.left,
31 | y: boundingBox.top,
32 | )
33 |
34 | shuffle(model.text)
35 |
36 |
37 | proc startTransform(elements: seq[Element]): () -> void =
38 | result = proc() =
39 | for element in elements:
40 | element.classList.add("animate-on-transform")
41 | element.style.transform = cstring""
42 |
43 |
44 | proc postRenderCallback(model: Model) =
45 | kout("post render".cstring)
46 |
47 | var deltas = newSeq[(Element, float, float)]()
48 |
49 | # using a two pass approach to avoid layout thrashing
50 | for word in model.text:
51 | var id = "word-" & $word.id
52 | var element = getElementById(id)
53 | if not element.isNil:
54 |
55 | if model.positions.contains(id):
56 | let boundingBox = element.getBoundingClientRect()
57 | let oldPos = model.positions[id]
58 | let newPos = Position(
59 | x: boundingBox.left,
60 | y: boundingBox.top,
61 | )
62 | let dx = oldPos.x - newPos.x
63 | let dy = oldPos.y - newPos.y
64 | deltas &= (element, dx, dy)
65 |
66 | # second pass: apply transforms
67 | for element, dx, dy in deltas.items():
68 | let transform = "translateX(" & $dx & "px) translateY(" & $dy & "px)"
69 | # kout(oldPos, newPos)
70 | element.style.transform = transform.cstring
71 |
72 | # request start of animation for affected elements
73 | let elements = deltas.map(x => x[0])
74 | reqFrame(startTransform(elements))
75 |
76 |
77 | proc view(model: Model): VNode =
78 | result = buildHtml():
79 | tdiv:
80 | button(class="button", onclick=curry(onClick, model)):
81 | text "Shuffle"
82 | tdiv:
83 | for word in model.text:
84 | let id = "word-" & $word.id
85 | span(class="word", id=id):
86 | text word.s.cstring
87 |
88 |
89 | proc runMain() =
90 |
91 | # A pity that `pairs` can't be chained. Would be nice to write:
92 | # let text = textOrig.cycle(10)
93 | # .pairs()
94 | # .map((i, w) => IdentifyableString(s: w, id: i))
95 | let textOrig = @["Entropy", "isn’t", "what", "it", "used", "to", "be."]
96 | let text = toSeq(pairs(textOrig.cycle(10))).map(
97 | t => IdentifyableString(s: t.val, id: t.key)
98 | )
99 |
100 | var model = Model(
101 | text: text,
102 | positions: newJDict[string, Position]()
103 | )
104 |
105 | proc renderer(): VNode =
106 | view(model)
107 |
108 | setRenderer renderer, curry(postRenderCallback, model)
109 |
110 | runMain()
111 |
112 |
--------------------------------------------------------------------------------