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 42b07a7..842e140 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,7 @@
nimcache/
-/tests/tester
\ No newline at end of file
+/tests/tester
+karax/tools/karun
+*.code-workspace
+*.exe
+app.js
+app.html
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0c60bd2..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-sudo: false
-language: c
-os: linux
-install:
- - git clone https://github.com/nim-lang/nim
- - cd nim
- - git clone --depth 1 https://github.com/nim-lang/csources.git
- - cd csources
- - sh build.sh
- - cd ../
- - bin/nim c koch
- - ./koch boot -d:release
- - cd ..
-before_script:
- - set -e
- - export PATH=$(pwd)/nim/bin:$(pwd):$PATH
-script:
- - nim c -r tests/tester.nim
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
index 979dd38..500e73a 100644
--- a/examples/carousel/carousel.html
+++ b/examples/carousel/carousel.html
@@ -1,15 +1,15 @@
-
- Carousel app
-
+
+ Carousel app
+
-
+
-
+
-
+
-
+
diff --git a/examples/carousel/carousel.nims b/examples/carousel/carousel.nims
index 1451fc0..cef2dec 100644
--- a/examples/carousel/carousel.nims
+++ b/examples/carousel/carousel.nims
@@ -1 +1 @@
---path: "../../src"
+--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 7bc9c65..c84b68b 100644
--- a/examples/mediaplayer/mediaplayer.nim
+++ b/examples/mediaplayer/mediaplayer.nim
@@ -1,5 +1,5 @@
-import karax, karaxdsl, vdom, kdom, compact
+import karax / [karax, karaxdsl, vdom, kdom, compact]
const
Play = 0
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 47ddeae..e3720fd 100644
--- a/examples/todoapp/todoapp.nim
+++ b/examples/todoapp/todoapp.nim
@@ -1,5 +1,5 @@
-import vdom, karax, karaxdsl, jstrutils, compact, 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,8 +41,9 @@ 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.index, cstring(nil), false)
@@ -61,9 +62,9 @@ proc toggleEntry(ev: Event; n: VNode) =
markAsCompleted(id, not isCompleted(id))
proc onAllDone(ev: Event; n: VNode) =
- clear()
- selectedEntry = -1
-
+ for i in 0..
-
+