diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..5e6c415
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@
+# This is a basic workflow to help you get started with Actions
+
+name: CI
+
+# Controls when the action will run.
+on:
+ # Triggers the workflow on push or pull request events but only for the master branch
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ build:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+ container: nimlang/nim
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v2
+ # Runs a single command using the runners shell
+ - name: Tests
+ run: |
+ nimble develop -y
+ nim c -r tests/tester.nim
diff --git a/.gitignore b/.gitignore
index 67d9b34..842e140 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,7 @@
nimcache/
+/tests/tester
+karax/tools/karun
+*.code-workspace
+*.exe
+app.js
+app.html
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..ac49796
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Xored Software, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/docs/docs.rst b/docs/docs.rst
index 8298ba9..3eec1a4 100644
--- a/docs/docs.rst
+++ b/docs/docs.rst
@@ -10,17 +10,17 @@ and some extensions that allow for an efficient component system.
These extensions are:
``VNodeKind.int``
- The node havs a single integer field.
+ The node has a single integer field.
``VNodeKind.bool``
The node has a single boolean field.
-``VNodeKint.vthunk``
+``VNodeKind.vthunk``
The node is a `virtual thunk`:idx:. This means there is a
function attached to it that produces the ``VNode`` structure
on demand.
-``VNodeKint.dthunk``
+``VNodeKind.dthunk``
The node is a `DOM thunk`:idx:. This means there is a
function attached to it that produces the ``Node`` DOM structure
on demand.
@@ -53,6 +53,6 @@ Event system
Karax does not abstract over the event system the DOM offers much: The same
``Event`` structure is used. Every callback has the
-signature ``proc (ev: Event; n: VNode)``.
+signature ``proc (ev: Event; n: VNode)`` or empty ``proc ()``.
diff --git a/examples/button.nim b/examples/button.nim
new file mode 100644
index 0000000..d86fa82
--- /dev/null
+++ b/examples/button.nim
@@ -0,0 +1,15 @@
+include karax / prelude
+
+var lines: seq[kstring] = @[]
+
+proc createDom(): VNode =
+ result = buildHtml(tdiv):
+ button:
+ text "Say hello!"
+ proc onclick(ev: Event; n: VNode) =
+ lines.add "Hello simulated universe"
+ for x in lines:
+ tdiv:
+ text x
+
+setRenderer createDom
diff --git a/examples/buttonlambda.nim b/examples/buttonlambda.nim
new file mode 100644
index 0000000..2ddadff
--- /dev/null
+++ b/examples/buttonlambda.nim
@@ -0,0 +1,14 @@
+include karax / prelude
+from future import `=>`
+
+var lines: seq[kstring] = @[]
+
+proc createDom(): VNode =
+ result = buildHtml(tdiv):
+ button(onclick = () => lines.add "Hello simulated universe"):
+ text "Say hello!"
+ for x in lines:
+ tdiv:
+ text x
+
+setRenderer createDom
diff --git a/examples/carousel/carousel.html b/examples/carousel/carousel.html
new file mode 100644
index 0000000..500e73a
--- /dev/null
+++ b/examples/carousel/carousel.html
@@ -0,0 +1,15 @@
+
+
+
+
+ Carousel app
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/carousel/carousel.nim b/examples/carousel/carousel.nim
new file mode 100644
index 0000000..c2db56c
--- /dev/null
+++ b/examples/carousel/carousel.nim
@@ -0,0 +1,69 @@
+## This demo shows how you can develop your own stateful components with Karax.
+
+import vdom, vstyles, karax, karaxdsl, jdict, jstrutils, kdom
+
+type
+ Carousel = ref object of VComponent
+ counter: int
+ cntdown: int
+ timer: TimeOut
+ list: seq[cstring]
+
+const ticksUntilChange = 5
+
+var
+ images: seq[cstring] = @[cstring"a", "b", "c", "d"]
+
+proc render(x: VComponent): VNode =
+ let self = Carousel(x)
+
+ proc docount() =
+ dec self.cntdown
+ if self.cntdown == 0:
+ self.counter = (self.counter + 1) mod self.list.len
+ self.cntdown = ticksUntilChange
+ else:
+ self.timer = setTimeout(docount, 30)
+ markDirty(self)
+ redraw()
+
+ proc onclick(ev: Event; n: VNode) =
+ if self.timer != nil:
+ clearTimeout(self.timer)
+ self.timer = setTimeout(docount, 30)
+
+ let col =
+ case self.counter
+ of 0: cstring"#4d4d4d"
+ of 1: cstring"#ff00ff"
+ of 2: cstring"#00ffff"
+ of 3: cstring"#ffff00"
+ else: cstring"red"
+ result = buildHtml(tdiv()):
+ text self.list[self.counter]
+ button(onclick = onclick):
+ text "Next"
+ tdiv(style = style(StyleAttr.color, col)):
+ text "This changes its color."
+ if self.cntdown != ticksUntilChange:
+ text &self.cntdown
+
+proc carousel(): Carousel =
+ result = newComponent(Carousel, render)
+ result.list = images
+ result.cntdown = ticksUntilChange
+
+proc createDom(): VNode =
+ result = buildHtml(table):
+ tr:
+ td:
+ carousel()
+ td:
+ carousel()
+ tr:
+ td:
+ carousel()
+ td:
+ carousel()
+
+setRenderer createDom
diff --git a/examples/carousel/carousel.nims b/examples/carousel/carousel.nims
new file mode 100644
index 0000000..cef2dec
--- /dev/null
+++ b/examples/carousel/carousel.nims
@@ -0,0 +1 @@
+--path: "../../karax"
diff --git a/examples/config.nims b/examples/config.nims
new file mode 100644
index 0000000..76ed4d9
--- /dev/null
+++ b/examples/config.nims
@@ -0,0 +1 @@
+--path: ".."
diff --git a/examples/hellostyle.nim b/examples/hellostyle.nim
new file mode 100644
index 0000000..716c2f0
--- /dev/null
+++ b/examples/hellostyle.nim
@@ -0,0 +1,17 @@
+include karax / prelude
+import karax / vstyles
+
+proc createDom(): VNode =
+ result = buildHtml(tdiv):
+ tdiv(style = style(StyleAttr.color, "red".cstring)):
+ text "red"
+ tdiv(style = style(StyleAttr.color, "blue")):
+ text "blue"
+ # explicit `kstring` required for varargs overload
+ tdiv(style = style((fontStyle, "italic".kstring), (color, "orange".kstring))):
+ text "italic orange"
+ # can use a string directly
+ tdiv(style = "font-style: oblique; color: pink".toCss):
+ text "oblique pink"
+
+setRenderer createDom
diff --git a/examples/hellouniverse.nim b/examples/hellouniverse.nim
new file mode 100644
index 0000000..8343d07
--- /dev/null
+++ b/examples/hellouniverse.nim
@@ -0,0 +1,12 @@
+include karax / prelude
+import random
+
+proc createDom(): VNode =
+ result = buildHtml(tdiv):
+ if rand(100) <= 50:
+ text "Hello World!"
+ else:
+ text "Hello Universe"
+
+randomize()
+setRenderer createDom
diff --git a/examples/helloworld.nim b/examples/helloworld.nim
new file mode 100644
index 0000000..76c43db
--- /dev/null
+++ b/examples/helloworld.nim
@@ -0,0 +1,7 @@
+include karax / prelude
+
+proc createDom(): VNode =
+ result = buildHtml(tdiv):
+ text "Hello World!"
+
+setRenderer createDom
diff --git a/examples/login.nim b/examples/login.nim
new file mode 100644
index 0000000..0350e23
--- /dev/null
+++ b/examples/login.nim
@@ -0,0 +1,46 @@
+include karax / prelude
+from sugar import `=>`
+import karax / errors
+
+proc loginField(desc, field, class: kstring;
+ validator: proc (field: kstring): proc ()): VNode =
+ result = buildHtml(tdiv):
+ label(`for` = field):
+ text desc
+ input(class = class, id = field, onchange = validator(field))
+
+# some consts in order to prevent typos:
+const
+ username = kstring"username"
+ password = kstring"password"
+
+proc validateNotEmpty(field: kstring): proc () =
+ result = proc () =
+ let x = getVNodeById(field)
+ if x.text == "":
+ errors.setError(field, field & " must not be empty")
+ else:
+ errors.setError(field, "")
+
+var loggedIn: bool
+
+proc loginDialog(): VNode =
+ result = buildHtml(tdiv):
+ if not loggedIn:
+ loginField("Name: ", username, "input", validateNotEmpty)
+ loginField("Password: ", password, "password", validateNotEmpty)
+ button(onclick = () => (loggedIn = true), disabled = errors.disableOnError()):
+ text "Login"
+ p:
+ text errors.getError(username)
+ p:
+ text errors.getError(password)
+ else:
+ p:
+ text "You are now logged in."
+
+setError username, username & " must not be empty"
+setError password, password & " must not be empty"
+
+when not declared(toychat):
+ setRenderer loginDialog
diff --git a/examples/mediaplayer/mediaplayer.nim b/examples/mediaplayer/mediaplayer.nim
index 890c078..c84b68b 100644
--- a/examples/mediaplayer/mediaplayer.nim
+++ b/examples/mediaplayer/mediaplayer.nim
@@ -1,5 +1,5 @@
-import karax, karaxdsl, vdom, kdom, components
+import karax / [karax, karaxdsl, vdom, kdom, compact]
const
Play = 0
@@ -12,10 +12,10 @@ proc play(n: Node) {.importcpp.}
proc pause(n: Node) {.importcpp.}
proc `width=`(n: Node, w: int) {.importcpp: "#.width = #".}
-proc mplayer*(id, resource: cstring): VNode {.component.} =
+proc mplayer*(id, resource: cstring): VNode {.compact.} =
proc handler(ev: Event; n: VNode) =
let myVideo = document.getElementById(id)
- case n.key
+ case n.index
of Play:
if myVideo.paused:
myVideo.play()
@@ -27,13 +27,13 @@ proc mplayer*(id, resource: cstring): VNode {.component.} =
else: discard
result = buildHtml(tdiv):
- button(onclick=handler, key=Play):
+ button(onclick=handler, index=Play):
text "Play/Pause"
- button(onclick=handler, key=Big):
+ button(onclick=handler, index=Big):
text "Big"
- button(onclick=handler, key=Small):
+ button(onclick=handler, index=Small):
text "Small"
- button(onclick=handler, key=Normal):
+ button(onclick=handler, index=Normal):
text "Normal"
br()
br()
diff --git a/examples/mediaplayer/playerapp.html b/examples/mediaplayer/playerapp.html
index 9e076e6..a45a2e8 100644
--- a/examples/mediaplayer/playerapp.html
+++ b/examples/mediaplayer/playerapp.html
@@ -1,15 +1,15 @@
-
- Player app
-
+
+ Player app
+
-
+
-
+
-
+
-
+
diff --git a/examples/mediaplayer/playerapp.nim b/examples/mediaplayer/playerapp.nim
index e611eec..acb2e68 100644
--- a/examples/mediaplayer/playerapp.nim
+++ b/examples/mediaplayer/playerapp.nim
@@ -1,6 +1,6 @@
## Example app that shows how to write and embed a custom component.
-include karaxprelude
+include karax/prelude
import mediaplayer
const url = "https://www.w3schools.com/html/mov_bbb.mp4"
diff --git a/examples/mediaplayer/playerapp.nims b/examples/mediaplayer/playerapp.nims
index 9028ff1..cf2eb9e 100644
--- a/examples/mediaplayer/playerapp.nims
+++ b/examples/mediaplayer/playerapp.nims
@@ -1 +1 @@
---path: "../../src"
+--path: "../.."
diff --git a/examples/scrollapp/scrollapp.html b/examples/scrollapp/scrollapp.html
index 1ff8b1b..44d657e 100644
--- a/examples/scrollapp/scrollapp.html
+++ b/examples/scrollapp/scrollapp.html
@@ -1,15 +1,15 @@
-
- Todo app
-
+
+ Todo app
+
-
+
-
+
-
+
-
+
diff --git a/examples/scrollapp/scrollapp.nim b/examples/scrollapp/scrollapp.nim
index 8ead611..4490447 100644
--- a/examples/scrollapp/scrollapp.nim
+++ b/examples/scrollapp/scrollapp.nim
@@ -1,7 +1,7 @@
## Example that shows how to accomplish an "infinitely scrolling" app.
-include karaxprelude
-import jstrutils, kdom, vstyles
+include karax/prelude
+import karax / [jstrutils, kdom, vstyles]
var entries: seq[cstring] = @[]
for i in 1..500:
diff --git a/examples/scrollapp/scrollapp.nims b/examples/scrollapp/scrollapp.nims
index 9028ff1..cf2eb9e 100644
--- a/examples/scrollapp/scrollapp.nims
+++ b/examples/scrollapp/scrollapp.nims
@@ -1 +1 @@
---path: "../../src"
+--path: "../.."
diff --git a/examples/todoapp/todoapp.html b/examples/todoapp/todoapp.html
index 488a3bf..ab3daa7 100644
--- a/examples/todoapp/todoapp.html
+++ b/examples/todoapp/todoapp.html
@@ -1,16 +1,16 @@
-
- Todo app
-
-
+
+ Todo app
+
+
-
+
-
+
-
+
-
+
diff --git a/examples/todoapp/todoapp.nim b/examples/todoapp/todoapp.nim
index 802bfc9..e3720fd 100644
--- a/examples/todoapp/todoapp.nim
+++ b/examples/todoapp/todoapp.nim
@@ -1,5 +1,5 @@
-import vdom, karax, karaxdsl, jstrutils, components, localstorage
+import karax / [vdom, karax, karaxdsl, jstrutils, compact, localstorage]
type
Filter = enum
@@ -9,7 +9,7 @@ var
selectedEntry = -1
filter: Filter
entriesLen: int
-
+ doneswitch = true
const
contentSuffix = cstring"content"
completedSuffix = cstring"completed"
@@ -41,29 +41,30 @@ proc updateEntry(pos: int, content: cstring, completed: bool) =
markAsCompleted(pos, completed)
proc onTodoEnter(ev: Event; n: VNode) =
- addEntry(n.value, false)
- n.value = ""
+ if n.value.strip() != "":
+ addEntry(n.value, false)
+ n.value = ""
proc removeHandler(ev: Event; n: VNode) =
- updateEntry(n.key, cstring(nil), false)
+ updateEntry(n.index, cstring(nil), false)
proc editHandler(ev: Event; n: VNode) =
- selectedEntry = n.key
+ selectedEntry = n.index
proc focusLost(ev: Event; n: VNode) = selectedEntry = -1
proc editEntry(ev: Event; n: VNode) =
- setEntryContent(n.key, n.value)
+ setEntryContent(n.index, n.value)
selectedEntry = -1
proc toggleEntry(ev: Event; n: VNode) =
- let id = n.key
+ let id = n.index
markAsCompleted(id, not isCompleted(id))
proc onAllDone(ev: Event; n: VNode) =
- clear()
- selectedEntry = -1
-
+ for i in 0..5VhNc9owEP01PjYjS8bGx4bQ9tDOZIZDk6OwN7YaYTFCfPXXV7ZkbGHDEEJgMuXASE+rlfR230rgkdFs813Sef5LpMA9jNKNRx48jKOBr79LYGuAIB4aIJMsNZDfABP2FyyILLpkKSwcQyUEV2zugokoCkiUg1Epxdo1exHcXXVOM+gAk4TyLvqbpSo36BCHDf4DWJbXK/thbEamNHnNpFgWdj0Pk5fqY4ZntPZlD7rIaSrWLYiMPTKSQijTmm1GwEtqa9rMvG8HRnf7llCoUyZEdhtqWx8dUs2E7QqpcpGJgvJxg95Xx4PSAdK9XM24bvq6CRumnkr4Dg9s97keKpTcmrFB3X22Hv6AUlubA3SphIaahX8KMbc+XhjnI8GFrDaqeYUwSTS+UFK8QmskjeIpKj2bw5UnOkiPhRZiKRNrhW3CUZmBtSK70OiMBzEDvX1tIoFTxVaud2pzL9vZNfzrhg1BfziGHxOO6Eg4AuwE5Au6Q35UI48gmT4EyM8TquBKobJLryhf7hWvVvTWOVMwmdNqv2tdKt0InUvTCqSCzXGiuhTUE4jdp63Nse2um0Ln1+UpbxW5EL2ftJqiFmseDrle4T5lK93MVHVIA03lPqL999j9D1Nny4XSxBX6Yug6mcLJ6+kLR+picfmTHalbhShOKlR1+9m9J57sjObSqMbOrUXDBPpFdj9EEYrOrkXk8rWomvpVSrptGcwFK9Si5fmxBFr6jl1948HeE2DPfifKfnvdMDtoBL47ykmaJ91C6WE9C5FPUS7DPXbwNcul36HoHF0hR1fYEZbvCgu/RVnn6SS43fMq6L2zy1z08adIRkJumY1Rz+X9jvT82DSLu2nmh1fKM//4O/5k3R79HRUH2NUuIuQNrPa+7y9G9bUUHR9UdPDm1/gF9BmgG76tw6PV7XacDK9Xs3S3+e/EvFWa/6fI+B8=
\ No newline at end of file
diff --git a/experiments/ajaxtest.html b/experiments/ajaxtest.html
index 39062ad..2927a18 100644
--- a/experiments/ajaxtest.html
+++ b/experiments/ajaxtest.html
@@ -9,5 +9,5 @@
-
+