move karax source code from 'src' to 'karax'; make karaxdsl work with native Nim; added jjson and jwebsockets modules
This commit is contained in:
parent
bb1732c834
commit
8e8ada7cd2
34 changed files with 407 additions and 203 deletions
|
|
@ -1 +1 @@
|
||||||
--path: "../../src"
|
--path: "../../karax"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
import karax, karaxdsl, vdom, kdom, compact
|
import karax / [karax, karaxdsl, vdom, kdom, compact]
|
||||||
|
|
||||||
const
|
const
|
||||||
Play = 0
|
Play = 0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
## Example app that shows how to write and embed a custom component.
|
## Example app that shows how to write and embed a custom component.
|
||||||
|
|
||||||
include karaxprelude
|
include karax/prelude
|
||||||
import mediaplayer
|
import mediaplayer
|
||||||
|
|
||||||
const url = "https://www.w3schools.com/html/mov_bbb.mp4"
|
const url = "https://www.w3schools.com/html/mov_bbb.mp4"
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
--path: "../../src"
|
--path: "../.."
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
## Example that shows how to accomplish an "infinitely scrolling" app.
|
## Example that shows how to accomplish an "infinitely scrolling" app.
|
||||||
|
|
||||||
include karaxprelude
|
include karax/prelude
|
||||||
import jstrutils, kdom, vstyles
|
import karax / [jstrutils, kdom, vstyles]
|
||||||
|
|
||||||
var entries: seq[cstring] = @[]
|
var entries: seq[cstring] = @[]
|
||||||
for i in 1..500:
|
for i in 1..500:
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
--path: "../../src"
|
--path: "../.."
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
import vdom, karax, karaxdsl, jstrutils, compact, localstorage
|
import karax / [vdom, karax, karaxdsl, jstrutils, compact, localstorage]
|
||||||
|
|
||||||
type
|
type
|
||||||
Filter = enum
|
Filter = enum
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
--path: "../../src"
|
--path: "../.."
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
author = "Xored Software, Inc."
|
author = "Xored Software, Inc."
|
||||||
description = "Karax is a framework for developing single page applications in Nim."
|
description = "Karax is a framework for developing single page applications in Nim."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
srcDir = "src"
|
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 0.16.1"
|
requires "nim >= 0.16.1"
|
||||||
|
|
||||||
skipDirs = @["examples/todoapp"]
|
skipDirs = @["examples"]
|
||||||
|
|
|
||||||
130
karax/compact.nim
Normal file
130
karax/compact.nim
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
## Components in Karax are built by the ``.component`` macro annotation.
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
import jdict, kdom
|
||||||
|
|
||||||
|
import macros, vdom, tables, strutils, kbase
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
var
|
||||||
|
vcomponents* = newJDict[cstring, proc(args: seq[VNode]): VNode]()
|
||||||
|
dcomponents* = newJDict[cstring, proc(args: seq[VNode]): Node]()
|
||||||
|
else:
|
||||||
|
var
|
||||||
|
vcomponents* = newTable[kstring, proc(args: seq[VNode]): VNode]()
|
||||||
|
dcomponents* = newTable[kstring, proc(args: seq[VNode]): Node]()
|
||||||
|
|
||||||
|
type
|
||||||
|
ComponentKind* {.pure.} = enum
|
||||||
|
None,
|
||||||
|
Tag,
|
||||||
|
VNode,
|
||||||
|
Node
|
||||||
|
|
||||||
|
var
|
||||||
|
allcomponents {.compileTime.} = initTable[string, ComponentKind]()
|
||||||
|
|
||||||
|
proc isComponent*(x: string): ComponentKind {.compileTime.} =
|
||||||
|
allcomponents.getOrDefault(x)
|
||||||
|
|
||||||
|
proc addTags() {.compileTime.} =
|
||||||
|
let x = (bindSym"VNodeKind").getTypeImpl
|
||||||
|
expectKind(x, nnkEnumTy)
|
||||||
|
for i in ord(VNodeKind.html)..ord(VNodeKind.high):
|
||||||
|
# +1 because of empty node at the start of the enum AST:
|
||||||
|
let tag = $x[i+1]
|
||||||
|
allcomponents[tag] = ComponentKind.Tag
|
||||||
|
|
||||||
|
static:
|
||||||
|
addTags()
|
||||||
|
|
||||||
|
proc unpack(symbolicType: NimNode; index: int): NimNode {.compileTime.} =
|
||||||
|
#let t = symbolicType.getTypeImpl
|
||||||
|
let t = repr(symbolicType)
|
||||||
|
case t
|
||||||
|
of "cstring":
|
||||||
|
result = quote do:
|
||||||
|
args[`index`].text
|
||||||
|
of "int", "VKey":
|
||||||
|
result = quote do:
|
||||||
|
args[`index`].intValue
|
||||||
|
of "bool":
|
||||||
|
result = quote do:
|
||||||
|
args[`index`].intValue != 0
|
||||||
|
elif t.endsWith"Kind":
|
||||||
|
result = quote do:
|
||||||
|
`symbolicType`(args[`index`].intValue)
|
||||||
|
else:
|
||||||
|
# just pass it along, maybe there is some conversion for it:
|
||||||
|
result = quote do:
|
||||||
|
args[`index`]
|
||||||
|
|
||||||
|
proc newname*(n: NimNode): NimNode =
|
||||||
|
if n.kind == nnkPostfix:
|
||||||
|
n[1] = newname(n[1])
|
||||||
|
result = n
|
||||||
|
elif n.kind == nnkSym:
|
||||||
|
result = ident($n.symbol)
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
macro compact*(prc: untyped): untyped =
|
||||||
|
## A 'compact' tree generation proc is one that only depends on its
|
||||||
|
## inputs and should be stored as a compact virtual DOM tree and
|
||||||
|
## only expanded on demand (when its inputs changed).
|
||||||
|
var n = prc.copyNimNode
|
||||||
|
for i in 0..6: n.add prc[i].copyNimTree
|
||||||
|
expectKind(n, nnkProcDef)
|
||||||
|
if n[0].kind == nnkEmpty:
|
||||||
|
error("please pass a non anonymous proc", n[0])
|
||||||
|
let name = n[0]
|
||||||
|
let params = params(n)
|
||||||
|
let rettype = repr params[0]
|
||||||
|
var isvirtual = ComponentKind.None
|
||||||
|
if rettype == "VNode":
|
||||||
|
isvirtual = ComponentKind.VNode
|
||||||
|
elif rettype == "Node":
|
||||||
|
isvirtual = ComponentKind.Node
|
||||||
|
else:
|
||||||
|
error "component must return VNode or Node", params[0]
|
||||||
|
let realName = if name.kind == nnkPostfix: name[1] else: name
|
||||||
|
let nn = $realName
|
||||||
|
n[0] = ident("inner" & nn)
|
||||||
|
var unpackCall = newCall(n[0])
|
||||||
|
var counter = 0
|
||||||
|
for i in 1.. <params.len:
|
||||||
|
let param = params[i]
|
||||||
|
let L = param.len
|
||||||
|
let typ = param[L-2]
|
||||||
|
for j in 0 .. L-3:
|
||||||
|
unpackCall.add unpack(typ, counter)
|
||||||
|
inc counter
|
||||||
|
|
||||||
|
template vwrapper(pname, unpackCall) {.dirty.} =
|
||||||
|
proc pname(args: seq[VNode]): VNode =
|
||||||
|
unpackCall
|
||||||
|
|
||||||
|
template dwrapper(pname, unpackCall) {.dirty.} =
|
||||||
|
proc pname(args: seq[VNode]): Node =
|
||||||
|
unpackCall
|
||||||
|
|
||||||
|
template vregister(key, val) =
|
||||||
|
bind jdict.`[]=`
|
||||||
|
`[]=`(vcomponents, kstring(key), val)
|
||||||
|
|
||||||
|
template dregister(key, val) =
|
||||||
|
bind jdict.`[]=`
|
||||||
|
`[]=`(dcomponents, kstring(key), val)
|
||||||
|
|
||||||
|
result = newTree(nnkStmtList, n)
|
||||||
|
|
||||||
|
if isvirtual == ComponentKind.VNode:
|
||||||
|
result.add getAst(vwrapper(newname name, unpackCall))
|
||||||
|
result.add getAst(vregister(newLit(nn), realName))
|
||||||
|
else:
|
||||||
|
result.add getAst(dwrapper(newname name, unpackCall))
|
||||||
|
result.add getAst(dregister(newLit(nn), realName))
|
||||||
|
allcomponents[nn] = isvirtual
|
||||||
|
when defined(debugKaraxDsl):
|
||||||
|
echo repr result
|
||||||
33
karax/jjson.nim
Normal file
33
karax/jjson.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
## This module implements some small zero-overhead 'JsonNode' type
|
||||||
|
## and helper that maps directly to JavaScript objects.
|
||||||
|
|
||||||
|
type
|
||||||
|
JsonNode* {.importc.} = ref object
|
||||||
|
|
||||||
|
proc `[]`*(obj: JsonNode; fieldname: cstring): JsonNode {.importcpp: "#[#]".}
|
||||||
|
proc `[]`*(obj: JsonNode; index: int): JsonNode {.importcpp: "#[#]".}
|
||||||
|
proc `[]=`*[T](obj: JsonNode; fieldname: cstring; value: T)
|
||||||
|
{.importcpp: "#[#] = #".}
|
||||||
|
proc length(x: JsonNode): int {.importcpp: "#.length".}
|
||||||
|
proc len*(x: JsonNode): int = (if x.isNil: 0 else: x.length)
|
||||||
|
|
||||||
|
proc newJsonNode*(fields: varargs[(cstring, JsonNode)]): JsonNode =
|
||||||
|
result = JsonNode()
|
||||||
|
for f in fields:
|
||||||
|
result[f[0]] = f[1]
|
||||||
|
|
||||||
|
proc newJObject*(): JsonNode =
|
||||||
|
result = JsonNode()
|
||||||
|
|
||||||
|
proc newJNull*(): JsonNode = nil
|
||||||
|
|
||||||
|
template `%`*(x: typed): JsonNode = cast[JsonNode](x)
|
||||||
|
|
||||||
|
proc getNum*(x: JsonNode): int {.importcpp: "#".}
|
||||||
|
|
||||||
|
proc getInt*(x: JsonNode): int {.importcpp: "#".}
|
||||||
|
proc getStr*(x: JsonNode): cstring {.importcpp: "#".}
|
||||||
|
proc getFNum*(x: JsonNode): cstring {.importcpp: "#".}
|
||||||
|
|
||||||
|
iterator items*(x: JsonNode): JsonNode =
|
||||||
|
for i in 0..<len(x): yield x[i]
|
||||||
13
karax/jwebsockets.nim
Normal file
13
karax/jwebsockets.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
## Websockets support for the JSON backend.
|
||||||
|
|
||||||
|
type
|
||||||
|
MessageEvent* {.importc.} = ref object
|
||||||
|
data*: cstring
|
||||||
|
|
||||||
|
WebSocket* {.importc.} = ref object
|
||||||
|
onmessage*: proc (e: MessageEvent)
|
||||||
|
|
||||||
|
proc newWebSocket*(url, key: cstring): WebSocket
|
||||||
|
{.importcpp: "new WebSocket(@)".}
|
||||||
|
|
||||||
|
proc send*(w: WebSocket; data: cstring) {.importcpp.}
|
||||||
|
|
@ -143,7 +143,6 @@ proc vnodeToDom*(n: VNode; kxi: KaraxInstance): Node =
|
||||||
return result
|
return result
|
||||||
elif n.kind == VNodeKind.component:
|
elif n.kind == VNodeKind.component:
|
||||||
let x = VComponent(n)
|
let x = VComponent(n)
|
||||||
if x.realDomImpl != nil: return x.realDomImpl(x)
|
|
||||||
if x.onAttachImpl != nil: x.onAttachImpl(x)
|
if x.onAttachImpl != nil: x.onAttachImpl(x)
|
||||||
assert x.renderImpl != nil
|
assert x.renderImpl != nil
|
||||||
if x.expanded == nil:
|
if x.expanded == nil:
|
||||||
|
|
@ -479,11 +478,7 @@ proc applyComponents(kxi: KaraxInstance) =
|
||||||
let newNode = kxi.components[i].newNode
|
let newNode = kxi.components[i].newNode
|
||||||
when defined(karaxDebug):
|
when defined(karaxDebug):
|
||||||
echo "Processing component ", newNode.text, " changed impl set ", x.changedImpl != nil
|
echo "Processing component ", newNode.text, " changed impl set ", x.changedImpl != nil
|
||||||
if x.realDomImpl != nil:
|
if x.changedImpl != nil and x.changedImpl(x, newNode):
|
||||||
let current = kxi.components[i].current
|
|
||||||
let parent = kxi.components[i].parent
|
|
||||||
kxi.addPatch(pkReplace, parent, current, x)
|
|
||||||
elif x.changedImpl != nil and x.changedImpl(x, newNode):
|
|
||||||
when defined(karaxDebug):
|
when defined(karaxDebug):
|
||||||
echo "Component ", newNode.text, " did change"
|
echo "Component ", newNode.text, " did change"
|
||||||
let current = kxi.components[i].current
|
let current = kxi.components[i].current
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
|
||||||
import macros, karax, vdom, compact
|
import macros, vdom, compact
|
||||||
from strutils import startsWith, toLowerAscii
|
from strutils import startsWith, toLowerAscii
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
import karax
|
||||||
|
|
||||||
const
|
const
|
||||||
StmtContext = ["kout", "inc", "echo", "dec", "!"]
|
StmtContext = ["kout", "inc", "echo", "dec", "!"]
|
||||||
SpecialAttrs = ["id", "class", "value", "index", "style"]
|
SpecialAttrs = ["id", "class", "value", "index", "style"]
|
||||||
|
|
@ -32,6 +35,11 @@ proc tcall2(n, tmpContext: NimNode): NimNode =
|
||||||
# (except for the last child of the macros we consider here),
|
# (except for the last child of the macros we consider here),
|
||||||
# lets, consts, types can be considered as expressions
|
# lets, consts, types can be considered as expressions
|
||||||
# case is complex, calls are assumed to produce a value.
|
# case is complex, calls are assumed to produce a value.
|
||||||
|
when defined(js):
|
||||||
|
template evHandler(): untyped = bindSym"addEventHandler"
|
||||||
|
else:
|
||||||
|
template evHandler(): untyped = ident"addEventHandler"
|
||||||
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nnkLiterals, nnkIdent, nnkSym, nnkDotExpr, nnkBracketExpr:
|
of nnkLiterals, nnkIdent, nnkSym, nnkDotExpr, nnkBracketExpr:
|
||||||
if tmpContext != nil:
|
if tmpContext != nil:
|
||||||
|
|
@ -68,7 +76,7 @@ proc tcall2(n, tmpContext: NimNode): NimNode =
|
||||||
if tmpContext == nil:
|
if tmpContext == nil:
|
||||||
error "no VNode to attach the event handler to"
|
error "no VNode to attach the event handler to"
|
||||||
else:
|
else:
|
||||||
result = newCall(bindSym"addEventHandler", tmpContext,
|
result = newCall(evHandler(), tmpContext,
|
||||||
newDotExpr(bindSym"EventKind", n[0]), anon, ident("kxi"))
|
newDotExpr(bindSym"EventKind", n[0]), anon, ident("kxi"))
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -94,7 +102,7 @@ proc tcall2(n, tmpContext: NimNode): NimNode =
|
||||||
if x.kind == nnkExprEqExpr:
|
if x.kind == nnkExprEqExpr:
|
||||||
let key = getName x[0]
|
let key = getName x[0]
|
||||||
if key.startsWith("on"):
|
if key.startsWith("on"):
|
||||||
result.add newCall(bindSym"addEventHandler",
|
result.add newCall(evHandler(),
|
||||||
tmp, newDotExpr(bindSym"EventKind", x[0]), x[1], ident("kxi"))
|
tmp, newDotExpr(bindSym"EventKind", x[0]), x[1], ident("kxi"))
|
||||||
elif key in SpecialAttrs:
|
elif key in SpecialAttrs:
|
||||||
result.add newDotAsgn(tmp, key, x[1])
|
result.add newDotAsgn(tmp, key, x[1])
|
||||||
8
karax/kbase.nim
Normal file
8
karax/kbase.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
## Karax -- Single page applications for Nim.
|
||||||
|
|
||||||
|
## Basic type definitions for Karax.
|
||||||
|
when defined(js):
|
||||||
|
type kstring* = cstring
|
||||||
|
else:
|
||||||
|
type kstring* = string
|
||||||
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
## Virtual DOM implementation for Karax.
|
## Virtual DOM implementation for Karax.
|
||||||
|
|
||||||
from kdom import Event, Node
|
when defined(js):
|
||||||
import shash, macros, vstyles
|
from kdom import Event, Node
|
||||||
|
else:
|
||||||
|
type
|
||||||
|
Event* = ref object
|
||||||
|
Node* = ref object
|
||||||
|
|
||||||
|
import macros, vstyles, kbase
|
||||||
from strutils import toUpperAscii
|
from strutils import toUpperAscii
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -88,15 +94,15 @@ macro buildLookupTables(): untyped =
|
||||||
for i in low(VNodeKind)..high(VNodeKind):
|
for i in low(VNodeKind)..high(VNodeKind):
|
||||||
let x = $i
|
let x = $i
|
||||||
let y = if x[0] == '#': x else: toUpperAscii(x)
|
let y = if x[0] == '#': x else: toUpperAscii(x)
|
||||||
a.add(newCall("cstring", newLit(y)))
|
a.add(newCall("kstring", newLit(y)))
|
||||||
var e = newTree(nnkBracket)
|
var e = newTree(nnkBracket)
|
||||||
for i in low(EventKind)..high(EventKind):
|
for i in low(EventKind)..high(EventKind):
|
||||||
e.add(newCall("cstring", newLit(substr($i, 2))))
|
e.add(newCall("kstring", newLit(substr($i, 2))))
|
||||||
|
|
||||||
template tmpl(a, e) {.dirty.} =
|
template tmpl(a, e) {.dirty.} =
|
||||||
const
|
const
|
||||||
toTag*: array[VNodeKind, cstring] = a
|
toTag*: array[VNodeKind, kstring] = a
|
||||||
toEventName*: array[EventKind, cstring] = e
|
toEventName*: array[EventKind, kstring] = e
|
||||||
|
|
||||||
result = getAst tmpl(a, e)
|
result = getAst tmpl(a, e)
|
||||||
|
|
||||||
|
|
@ -108,15 +114,15 @@ type
|
||||||
|
|
||||||
EventHandlers* = seq[(EventKind, EventHandler, NativeEventHandler)]
|
EventHandlers* = seq[(EventKind, EventHandler, NativeEventHandler)]
|
||||||
|
|
||||||
VKey* = cstring
|
VKey* = kstring
|
||||||
|
|
||||||
VNode* = ref object of RootObj
|
VNode* = ref object of RootObj
|
||||||
kind*: VNodeKind
|
kind*: VNodeKind
|
||||||
index*: int ## a generally useful 'index'
|
index*: int ## a generally useful 'index'
|
||||||
id*, class*, text*: cstring
|
id*, class*, text*: kstring
|
||||||
kids: seq[VNode]
|
kids: seq[VNode]
|
||||||
# even index: key, odd index: value; done this way for memory efficiency:
|
# even index: key, odd index: value; done this way for memory efficiency:
|
||||||
attrs: seq[cstring]
|
attrs: seq[kstring]
|
||||||
events*: EventHandlers
|
events*: EventHandlers
|
||||||
when false:
|
when false:
|
||||||
hash*: Hash
|
hash*: Hash
|
||||||
|
|
@ -133,7 +139,6 @@ type
|
||||||
updatedImpl*: proc(self, newInstance: VComponent)
|
updatedImpl*: proc(self, newInstance: VComponent)
|
||||||
onAttachImpl*: proc(self: VComponent)
|
onAttachImpl*: proc(self: VComponent)
|
||||||
onDetachImpl*: proc(self: VComponent)
|
onDetachImpl*: proc(self: VComponent)
|
||||||
realDomImpl*: proc(self: VComponent): kdom.Node
|
|
||||||
version*: int ## Update this to trigger a redraw by karax. Usually you
|
version*: int ## Update this to trigger a redraw by karax. Usually you
|
||||||
## should call 'markDirty' instead which is an alias for
|
## should call 'markDirty' instead which is an alias for
|
||||||
## 'inc version'.
|
## 'inc version'.
|
||||||
|
|
@ -143,22 +148,22 @@ type
|
||||||
## expanded to.
|
## expanded to.
|
||||||
debugId*: int
|
debugId*: int
|
||||||
|
|
||||||
proc value*(n: VNode): cstring = n.text
|
proc value*(n: VNode): kstring = n.text
|
||||||
proc `value=`*(n: VNode; v: cstring) = n.text = v
|
proc `value=`*(n: VNode; v: kstring) = n.text = v
|
||||||
|
|
||||||
proc intValue*(n: VNode): int = n.index
|
proc intValue*(n: VNode): int = n.index
|
||||||
proc vn*(i: int): VNode = VNode(kind: VNodeKind.int, index: i)
|
proc vn*(i: int): VNode = VNode(kind: VNodeKind.int, index: i)
|
||||||
proc vn*(b: bool): VNode = VNode(kind: VNodeKind.int, index: ord(b))
|
proc vn*(b: bool): VNode = VNode(kind: VNodeKind.int, index: ord(b))
|
||||||
proc vn*(x: cstring): VNode = VNode(kind: VNodeKind.text, index: -1, text: x)
|
proc vn*(x: kstring): VNode = VNode(kind: VNodeKind.text, index: -1, text: x)
|
||||||
|
|
||||||
template callThunk*(fn: typed; n: VNode): untyped =
|
template callThunk*(fn: typed; n: VNode): untyped =
|
||||||
## for internal usage only.
|
## for internal usage only.
|
||||||
fn(n.kids)
|
fn(n.kids)
|
||||||
|
|
||||||
proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
|
proc vthunk*(name: kstring; args: varargs[VNode, vn]): VNode =
|
||||||
VNode(kind: VNodeKind.vthunk, text: name, index: -1, kids: @args)
|
VNode(kind: VNodeKind.vthunk, text: name, index: -1, kids: @args)
|
||||||
|
|
||||||
proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
|
proc dthunk*(name: kstring; args: varargs[VNode, vn]): VNode =
|
||||||
VNode(kind: VNodeKind.dthunk, text: name, index: -1, kids: @args)
|
VNode(kind: VNodeKind.dthunk, text: name, index: -1, kids: @args)
|
||||||
|
|
||||||
proc setEventIfNoConflict(v: VNode; kind: EventKind; handler: EventHandler) =
|
proc setEventIfNoConflict(v: VNode; kind: EventKind; handler: EventHandler) =
|
||||||
|
|
@ -193,7 +198,7 @@ template newComponent*[T](t: typeDesc[T];
|
||||||
updated: proc(self, newInstance: VComponent) = defaultUpdatedImpl): T =
|
updated: proc(self, newInstance: VComponent) = defaultUpdatedImpl): T =
|
||||||
## Use this template to create new components.
|
## Use this template to create new components.
|
||||||
T(kind: VNodeKind.component, index: -1,
|
T(kind: VNodeKind.component, index: -1,
|
||||||
text: cstring(astToStr(t)), renderImpl: render,
|
text: kstring(astToStr(t)), renderImpl: render,
|
||||||
changedImpl: changed, updatedImpl: updated,
|
changedImpl: changed, updatedImpl: updated,
|
||||||
onAttachImpl: onAttach, onDetachImpl: onDetach,
|
onAttachImpl: onAttach, onDetachImpl: onDetach,
|
||||||
debugId: getDebugId())
|
debugId: getDebugId())
|
||||||
|
|
@ -202,7 +207,7 @@ template markDirty*(c: VComponent) =
|
||||||
## mark the component as dirty so that it is re-rendered.
|
## mark the component as dirty so that it is re-rendered.
|
||||||
inc c.version
|
inc c.version
|
||||||
|
|
||||||
proc setAttr*(n: VNode; key: cstring; val: cstring = "") =
|
proc setAttr*(n: VNode; key: kstring; val: kstring = "") =
|
||||||
if n.attrs.isNil:
|
if n.attrs.isNil:
|
||||||
n.attrs = @[key, val]
|
n.attrs = @[key, val]
|
||||||
else:
|
else:
|
||||||
|
|
@ -213,7 +218,7 @@ proc setAttr*(n: VNode; key: cstring; val: cstring = "") =
|
||||||
n.attrs.add key
|
n.attrs.add key
|
||||||
n.attrs.add val
|
n.attrs.add val
|
||||||
|
|
||||||
proc getAttr*(n: VNode; key: cstring): cstring =
|
proc getAttr*(n: VNode; key: kstring): kstring =
|
||||||
for i in countup(0, n.attrs.len-2, 2):
|
for i in countup(0, n.attrs.len-2, 2):
|
||||||
if n.attrs[i] == key: return n.attrs[i+1]
|
if n.attrs[i] == key: return n.attrs[i+1]
|
||||||
|
|
||||||
|
|
@ -237,7 +242,12 @@ proc takeOverFields*(newNode, oldNode: VNode) =
|
||||||
proc len*(x: VNode): int = x.kids.len
|
proc len*(x: VNode): int = x.kids.len
|
||||||
proc `[]`*(x: VNode; idx: int): VNode = x.kids[idx]
|
proc `[]`*(x: VNode; idx: int): VNode = x.kids[idx]
|
||||||
proc `[]=`*(x: VNode; idx: int; y: VNode) = x.kids[idx] = y
|
proc `[]=`*(x: VNode; idx: int; y: VNode) = x.kids[idx] = y
|
||||||
proc add*(parent, kid: VNode) = parent.kids.add kid
|
|
||||||
|
proc add*(parent, kid: VNode) =
|
||||||
|
when not defined(js) and not defined(nimNoNil):
|
||||||
|
if parent.kids.isNil: parent.kids = @[]
|
||||||
|
parent.kids.add kid
|
||||||
|
|
||||||
proc delete*(parent: VNode; position: int) =
|
proc delete*(parent: VNode; position: int) =
|
||||||
parent.kids.delete(position)
|
parent.kids.delete(position)
|
||||||
proc insert*(parent, kid: VNode; position: int) =
|
proc insert*(parent, kid: VNode; position: int) =
|
||||||
|
|
@ -248,18 +258,19 @@ proc tree*(kind: VNodeKind; kids: varargs[VNode]): VNode =
|
||||||
result = newVNode(kind)
|
result = newVNode(kind)
|
||||||
for k in kids: result.add k
|
for k in kids: result.add k
|
||||||
|
|
||||||
proc tree*(kind: VNodeKind; attrs: openarray[(cstring, cstring)];
|
proc tree*(kind: VNodeKind; attrs: openarray[(kstring, kstring)];
|
||||||
kids: varargs[VNode]): VNode =
|
kids: varargs[VNode]): VNode =
|
||||||
result = tree(kind, kids)
|
result = tree(kind, kids)
|
||||||
for a in attrs: result.setAttr(a[0], a[1])
|
for a in attrs: result.setAttr(a[0], a[1])
|
||||||
|
|
||||||
proc text*(s: string): VNode = VNode(kind: VNodeKind.text, text: cstring(s), index: -1)
|
when defined(js):
|
||||||
proc text*(s: cstring): VNode = VNode(kind: VNodeKind.text, text: s, index: -1)
|
proc text*(s: string): VNode = VNode(kind: VNodeKind.text, text: kstring(s), index: -1)
|
||||||
|
proc text*(s: kstring): VNode = VNode(kind: VNodeKind.text, text: s, index: -1)
|
||||||
|
|
||||||
iterator items*(n: VNode): VNode =
|
iterator items*(n: VNode): VNode =
|
||||||
for i in 0..<n.kids.len: yield n.kids[i]
|
for i in 0..<n.kids.len: yield n.kids[i]
|
||||||
|
|
||||||
iterator attrs*(n: VNode): (cstring, cstring) =
|
iterator attrs*(n: VNode): (kstring, kstring) =
|
||||||
for i in countup(0, n.attrs.len-2, 2):
|
for i in countup(0, n.attrs.len-2, 2):
|
||||||
yield (n.attrs[i], n.attrs[i+1])
|
yield (n.attrs[i], n.attrs[i+1])
|
||||||
|
|
||||||
|
|
@ -324,7 +335,91 @@ when false:
|
||||||
h &= child.hash
|
h &= child.hash
|
||||||
n.hash = h
|
n.hash = h
|
||||||
|
|
||||||
proc `$`*(n: VNode): cstring =
|
|
||||||
var res = ""
|
proc add*(result: var string, n: VNode, indent = 0, indWidth = 2) =
|
||||||
toString(n, res, 0)
|
## adds the textual representation of `n` to `result`.
|
||||||
result = cstring(res)
|
|
||||||
|
proc addEscapedAttr(result: var string, s: kstring) =
|
||||||
|
# `addEscaped` alternative with less escaped characters.
|
||||||
|
# Only to be used for escaping attribute values enclosed in double quotes!
|
||||||
|
for c in items(s):
|
||||||
|
case c
|
||||||
|
of '<': result.add("<")
|
||||||
|
of '>': result.add(">")
|
||||||
|
of '&': result.add("&")
|
||||||
|
of '"': result.add(""")
|
||||||
|
else: result.add(c)
|
||||||
|
|
||||||
|
proc addEscaped(result: var string, s: kstring) =
|
||||||
|
## same as ``result.add(escape(s))``, but more efficient.
|
||||||
|
for c in items(s):
|
||||||
|
case c
|
||||||
|
of '<': result.add("<")
|
||||||
|
of '>': result.add(">")
|
||||||
|
of '&': result.add("&")
|
||||||
|
of '"': result.add(""")
|
||||||
|
of '\'': result.add("'")
|
||||||
|
of '/': result.add("/")
|
||||||
|
else: result.add(c)
|
||||||
|
|
||||||
|
proc addIndent(result: var string, indent: int) =
|
||||||
|
result.add("\n")
|
||||||
|
for i in 1..indent: result.add(' ')
|
||||||
|
|
||||||
|
if n.kind == VNodeKind.text:
|
||||||
|
result.addEscaped(n.text)
|
||||||
|
else:
|
||||||
|
let kind = $n.kind
|
||||||
|
result.add('<')
|
||||||
|
result.add(kind)
|
||||||
|
if n.id != nil:
|
||||||
|
result.add " id=\""
|
||||||
|
result.addEscapedAttr(n.id)
|
||||||
|
result.add('"')
|
||||||
|
if n.class != nil:
|
||||||
|
result.add " class=\""
|
||||||
|
result.addEscapedAttr(n.class)
|
||||||
|
result.add('"')
|
||||||
|
for k, v in attrs(n):
|
||||||
|
result.add(' ')
|
||||||
|
result.add(k)
|
||||||
|
result.add("=\"")
|
||||||
|
result.addEscapedAttr(v)
|
||||||
|
result.add('"')
|
||||||
|
# XXX add style to string
|
||||||
|
if n.len > 0:
|
||||||
|
result.add('>')
|
||||||
|
if n.len > 1:
|
||||||
|
var noWhitespace = false
|
||||||
|
for i in 0..<n.len:
|
||||||
|
if n[i].kind == VNodeKind.text:
|
||||||
|
noWhitespace = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if noWhitespace:
|
||||||
|
# for mixed leaves, we cannot output whitespace for readability,
|
||||||
|
# because this would be wrong. For example: ``a<b>b</b>`` is
|
||||||
|
# different from ``a <b>b</b>``.
|
||||||
|
for i in 0..<n.len: result.add(n[i], indent+indWidth, indWidth)
|
||||||
|
else:
|
||||||
|
for i in 0..<n.len:
|
||||||
|
result.addIndent(indent+indWidth)
|
||||||
|
result.add(n[i], indent+indWidth, indWidth)
|
||||||
|
result.addIndent(indent)
|
||||||
|
else:
|
||||||
|
result.add(n[0], indent+indWidth, indWidth)
|
||||||
|
result.add("</")
|
||||||
|
result.add(kind)
|
||||||
|
result.add(">")
|
||||||
|
else:
|
||||||
|
result.add(" />")
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(n: VNode): kstring =
|
||||||
|
when defined(js):
|
||||||
|
var res = ""
|
||||||
|
toString(n, res, 0)
|
||||||
|
result = kstring(res)
|
||||||
|
else:
|
||||||
|
result = ""
|
||||||
|
add(result, n)
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
|
||||||
import kdom, macros, jdict
|
import macros, kbase
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
import kdom, jdict
|
||||||
|
|
||||||
type
|
type
|
||||||
StyleAttr* {.pure.} = enum
|
StyleAttr* {.pure.} = enum
|
||||||
|
|
@ -200,10 +203,10 @@ type
|
||||||
macro buildLookupTables(): untyped =
|
macro buildLookupTables(): untyped =
|
||||||
var e = newTree(nnkBracket)
|
var e = newTree(nnkBracket)
|
||||||
for i in low(StyleAttr)..high(StyleAttr):
|
for i in low(StyleAttr)..high(StyleAttr):
|
||||||
e.add(newCall("cstring", newLit($i)))
|
e.add(newCall("kstring", newLit($i)))
|
||||||
template tmpl(e) {.dirty.} =
|
template tmpl(e) {.dirty.} =
|
||||||
const
|
const
|
||||||
toStyleAttrName: array[StyleAttr, cstring] = e
|
toStyleAttrName: array[StyleAttr, kstring] = e
|
||||||
result = getAst tmpl(e)
|
result = getAst tmpl(e)
|
||||||
|
|
||||||
buildLookupTables()
|
buildLookupTables()
|
||||||
|
|
@ -212,8 +215,14 @@ buildLookupTables()
|
||||||
# it's relevant.
|
# it's relevant.
|
||||||
# even index: key, odd index: value; done this way for memory efficiency:
|
# even index: key, odd index: value; done this way for memory efficiency:
|
||||||
|
|
||||||
type
|
when defined(js):
|
||||||
VStyle* = JSeq[cstring]
|
type
|
||||||
|
VStyle* = JSeq[cstring]
|
||||||
|
else:
|
||||||
|
type
|
||||||
|
VStyle* = ref seq[string]
|
||||||
|
proc len*(a: VStyle): int = len(a[])
|
||||||
|
proc add*(a: VStyle; x: string) = add(a[], x)
|
||||||
|
|
||||||
proc eq*(a, b: VStyle): bool =
|
proc eq*(a, b: VStyle): bool =
|
||||||
if a.isNil:
|
if a.isNil:
|
||||||
|
|
@ -225,9 +234,7 @@ proc eq*(a, b: VStyle): bool =
|
||||||
if a[i] != b[i]: return false
|
if a[i] != b[i]: return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc kout[T](x: T) {.importc: "console.log", varargs.}
|
proc setAttr(s: VStyle; a, value: kstring) {.noSideEffect.} =
|
||||||
|
|
||||||
proc setAttr(s: VStyle; a, value: cstring) {.noSideEffect.} =
|
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
if s[i] == a:
|
if s[i] == a:
|
||||||
|
|
@ -247,11 +254,11 @@ proc setAttr(s: VStyle; a, value: cstring) {.noSideEffect.} =
|
||||||
s.add a
|
s.add a
|
||||||
s.add value
|
s.add value
|
||||||
|
|
||||||
proc setAttr*(s: VStyle; attr: StyleAttr, value: cstring) {.noSideEffect.} =
|
proc setAttr*(s: VStyle; attr: StyleAttr, value: kstring) {.noSideEffect.} =
|
||||||
assert value != nil, "value must not be nil"
|
assert value != nil, "value must not be nil"
|
||||||
setAttr(s, toStyleAttrName[attr], value)
|
setAttr(s, toStyleAttrName[attr], value)
|
||||||
|
|
||||||
proc getAttr*(s: VStyle; attr: StyleAttr): cstring {.noSideEffect.} =
|
proc getAttr*(s: VStyle; attr: StyleAttr): kstring {.noSideEffect.} =
|
||||||
## returns 'nil' if the attribute has not been set.
|
## returns 'nil' if the attribute has not been set.
|
||||||
var i = 0
|
var i = 0
|
||||||
let a = toStyleAttrName[attr]
|
let a = toStyleAttrName[attr]
|
||||||
|
|
@ -262,44 +269,60 @@ proc getAttr*(s: VStyle; attr: StyleAttr): cstring {.noSideEffect.} =
|
||||||
return nil
|
return nil
|
||||||
inc i, 2
|
inc i, 2
|
||||||
|
|
||||||
proc style*(pairs: varargs[(StyleAttr, cstring)]): VStyle {.noSideEffect.} =
|
proc style*(pairs: varargs[(StyleAttr, kstring)]): VStyle {.noSideEffect.} =
|
||||||
## constructs a VStyle object from a list of (attribute, value)-pairs.
|
## constructs a VStyle object from a list of (attribute, value)-pairs.
|
||||||
result = newJSeq[cstring]()
|
when defined(js):
|
||||||
|
result = newJSeq[cstring]()
|
||||||
|
else:
|
||||||
|
new(result)
|
||||||
|
result[] = @[]
|
||||||
for x in pairs:
|
for x in pairs:
|
||||||
result.setAttr x[0], x[1]
|
result.setAttr x[0], x[1]
|
||||||
|
|
||||||
proc style*(a: StyleAttr; val: cstring): VStyle {.noSideEffect.} =
|
proc style*(a: StyleAttr; val: kstring): VStyle {.noSideEffect.} =
|
||||||
## constructs a VStyle object from a single (attribute, value)-pair.
|
## constructs a VStyle object from a single (attribute, value)-pair.
|
||||||
result = newJSeq[cstring]()
|
when defined(js):
|
||||||
|
result = newJSeq[cstring]()
|
||||||
|
else:
|
||||||
|
new(result)
|
||||||
|
result[] = @[]
|
||||||
result.setAttr a, val
|
result.setAttr a, val
|
||||||
|
|
||||||
proc setStyle(d: Style; key, val: cstring) {.importcpp: "#[#] = #", noSideEffect.}
|
when defined(js):
|
||||||
|
proc setStyle(d: Style; key, val: cstring) {.importcpp: "#[#] = #", noSideEffect.}
|
||||||
|
|
||||||
|
proc applyStyle*(n: Node; s: VStyle) {.noSideEffect.} =
|
||||||
|
## apply the style to the real DOM node ``n``.
|
||||||
|
|
||||||
|
#n.style = Style() # optimized, this is a hotspot:
|
||||||
|
{.emit: "`n`.style = {};".}
|
||||||
|
for i in countup(0, s.len-1, 2):
|
||||||
|
n.style.setStyle(s[i], s[i+1])
|
||||||
|
|
||||||
proc merge*(a, b: VStyle): VStyle {.noSideEffect.} =
|
proc merge*(a, b: VStyle): VStyle {.noSideEffect.} =
|
||||||
## merges two styles. ``b`` takes precedence over ``a``.
|
## merges two styles. ``b`` takes precedence over ``a``.
|
||||||
result = newJSeq[cstring]()
|
when defined(js):
|
||||||
|
result = newJSeq[cstring]()
|
||||||
|
else:
|
||||||
|
new(result)
|
||||||
|
result[] = @[]
|
||||||
for i in 0..<a.len:
|
for i in 0..<a.len:
|
||||||
result.add a[i]
|
result.add a[i]
|
||||||
for i in countup(0, b.len-1, 2):
|
for i in countup(0, b.len-1, 2):
|
||||||
setAttr(result, b[i], b[i+1])
|
setAttr(result, b[i], b[i+1])
|
||||||
|
|
||||||
proc applyStyle*(n: Node; s: VStyle) {.noSideEffect.} =
|
iterator pairs*(s: VStyle): (kstring, kstring) {.noSideEffect.} =
|
||||||
## apply the style to the real DOM node ``n``.
|
|
||||||
|
|
||||||
#n.style = Style() # optimized, this is a hotspot:
|
|
||||||
{.emit: "`n`.style = {};".}
|
|
||||||
for i in countup(0, s.len-1, 2):
|
|
||||||
n.style.setStyle(s[i], s[i+1])
|
|
||||||
|
|
||||||
iterator pairs*(s: VStyle): (cstring, cstring) {.noSideEffect.} =
|
|
||||||
if s != nil:
|
if s != nil:
|
||||||
for i in countup(0, s.len-1, 2):
|
for i in countup(0, s.len-1, 2):
|
||||||
yield (s[i], s[i+1])
|
yield (s[i], s[i+1])
|
||||||
|
|
||||||
import jstrutils
|
when defined(js):
|
||||||
|
import jstrutils
|
||||||
|
else:
|
||||||
|
template `&`(x: untyped): untyped = $x
|
||||||
|
|
||||||
proc rgb*(r, g, b: range[0..255]): cstring =
|
proc rgb*(r, g, b: range[0..255]): kstring =
|
||||||
cstring"rgb(" & &r & cstring", " & &g & cstring", " & &b & cstring")"
|
kstring"rgb(" & &r & kstring", " & &g & kstring", " & &b & kstring")"
|
||||||
|
|
||||||
proc rgba*(r, g, b: range[0..255], alpha: float): cstring =
|
proc rgba*(r, g, b: range[0..255], alpha: float): kstring =
|
||||||
cstring"rgba(" & &r & cstring", " & &g & cstring", " & &b & cstring", " & &alpha & cstring")"
|
kstring"rgba(" & &r & kstring", " & &g & kstring", " & &b & kstring", " & &alpha & kstring")"
|
||||||
121
src/compact.nim
121
src/compact.nim
|
|
@ -1,121 +0,0 @@
|
||||||
## Components in Karax are built by the ``.component`` macro annotation.
|
|
||||||
|
|
||||||
import macros, jdict, kdom, vdom, tables, strutils
|
|
||||||
|
|
||||||
var
|
|
||||||
vcomponents* = newJDict[cstring, proc(args: seq[VNode]): VNode]()
|
|
||||||
dcomponents* = newJDict[cstring, proc(args: seq[VNode]): Node]()
|
|
||||||
|
|
||||||
type
|
|
||||||
ComponentKind* {.pure.} = enum
|
|
||||||
None,
|
|
||||||
Tag,
|
|
||||||
VNode,
|
|
||||||
Node
|
|
||||||
|
|
||||||
var
|
|
||||||
allcomponents {.compileTime.} = initTable[string, ComponentKind]()
|
|
||||||
|
|
||||||
proc isComponent*(x: string): ComponentKind {.compileTime.} =
|
|
||||||
allcomponents.getOrDefault(x)
|
|
||||||
|
|
||||||
proc addTags() {.compileTime.} =
|
|
||||||
let x = (bindSym"VNodeKind").getTypeImpl
|
|
||||||
expectKind(x, nnkEnumTy)
|
|
||||||
for i in ord(VNodeKind.html)..ord(VNodeKind.high):
|
|
||||||
# +1 because of empty node at the start of the enum AST:
|
|
||||||
let tag = $x[i+1]
|
|
||||||
allcomponents[tag] = ComponentKind.Tag
|
|
||||||
|
|
||||||
static:
|
|
||||||
addTags()
|
|
||||||
|
|
||||||
proc unpack(symbolicType: NimNode; index: int): NimNode {.compileTime.} =
|
|
||||||
#let t = symbolicType.getTypeImpl
|
|
||||||
let t = repr(symbolicType)
|
|
||||||
case t
|
|
||||||
of "cstring":
|
|
||||||
result = quote do:
|
|
||||||
args[`index`].text
|
|
||||||
of "int", "VKey":
|
|
||||||
result = quote do:
|
|
||||||
args[`index`].intValue
|
|
||||||
of "bool":
|
|
||||||
result = quote do:
|
|
||||||
args[`index`].intValue != 0
|
|
||||||
elif t.endsWith"Kind":
|
|
||||||
result = quote do:
|
|
||||||
`symbolicType`(args[`index`].intValue)
|
|
||||||
else:
|
|
||||||
# just pass it along, maybe there is some conversion for it:
|
|
||||||
result = quote do:
|
|
||||||
args[`index`]
|
|
||||||
|
|
||||||
proc newname*(n: NimNode): NimNode =
|
|
||||||
if n.kind == nnkPostfix:
|
|
||||||
n[1] = newname(n[1])
|
|
||||||
result = n
|
|
||||||
elif n.kind == nnkSym:
|
|
||||||
result = ident($n.symbol)
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
|
|
||||||
macro compact*(prc: untyped): untyped =
|
|
||||||
## A 'compact' tree generation proc is one that only depends on its
|
|
||||||
## inputs and should be stored as a compact virtual DOM tree and
|
|
||||||
## only expanded on demand (when its inputs changed).
|
|
||||||
var n = prc.copyNimNode
|
|
||||||
for i in 0..6: n.add prc[i].copyNimTree
|
|
||||||
expectKind(n, nnkProcDef)
|
|
||||||
if n[0].kind == nnkEmpty:
|
|
||||||
error("please pass a non anonymous proc", n[0])
|
|
||||||
let name = n[0]
|
|
||||||
let params = params(n)
|
|
||||||
let rettype = repr params[0]
|
|
||||||
var isvirtual = ComponentKind.None
|
|
||||||
if rettype == "VNode":
|
|
||||||
isvirtual = ComponentKind.VNode
|
|
||||||
elif rettype == "Node":
|
|
||||||
isvirtual = ComponentKind.Node
|
|
||||||
else:
|
|
||||||
error "component must return VNode or Node", params[0]
|
|
||||||
let realName = if name.kind == nnkPostfix: name[1] else: name
|
|
||||||
let nn = $realName
|
|
||||||
n[0] = ident("inner" & nn)
|
|
||||||
var unpackCall = newCall(n[0])
|
|
||||||
var counter = 0
|
|
||||||
for i in 1.. <params.len:
|
|
||||||
let param = params[i]
|
|
||||||
let L = param.len
|
|
||||||
let typ = param[L-2]
|
|
||||||
for j in 0 .. L-3:
|
|
||||||
unpackCall.add unpack(typ, counter)
|
|
||||||
inc counter
|
|
||||||
|
|
||||||
template vwrapper(pname, unpackCall) {.dirty.} =
|
|
||||||
proc pname(args: seq[VNode]): VNode =
|
|
||||||
unpackCall
|
|
||||||
|
|
||||||
template dwrapper(pname, unpackCall) {.dirty.} =
|
|
||||||
proc pname(args: seq[VNode]): Node =
|
|
||||||
unpackCall
|
|
||||||
|
|
||||||
template vregister(key, val) =
|
|
||||||
bind jdict.`[]=`
|
|
||||||
`[]=`(vcomponents, cstring(key), val)
|
|
||||||
|
|
||||||
template dregister(key, val) =
|
|
||||||
bind jdict.`[]=`
|
|
||||||
`[]=`(dcomponents, cstring(key), val)
|
|
||||||
|
|
||||||
result = newTree(nnkStmtList, n)
|
|
||||||
|
|
||||||
if isvirtual == ComponentKind.VNode:
|
|
||||||
result.add getAst(vwrapper(newname name, unpackCall))
|
|
||||||
result.add getAst(vregister(newLit(nn), realName))
|
|
||||||
else:
|
|
||||||
result.add getAst(dwrapper(newname name, unpackCall))
|
|
||||||
result.add getAst(dregister(newLit(nn), realName))
|
|
||||||
allcomponents[nn] = isvirtual
|
|
||||||
when defined(debugKaraxDsl):
|
|
||||||
echo repr result
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
include karaxprelude
|
include ../karax/prelude
|
||||||
|
|
||||||
block:
|
block:
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
include "../src/karax"
|
include "../karax/karax"
|
||||||
import "../src/karaxdsl"
|
import "../karax/karaxdsl"
|
||||||
|
|
||||||
proc hasDom(n: Vnode) =
|
proc hasDom(n: Vnode) =
|
||||||
if n.kind in {VNodeKind.component, VNodeKind.vthunk, VNodeKind.dthunk}:
|
if n.kind in {VNodeKind.component, VNodeKind.vthunk, VNodeKind.dthunk}:
|
||||||
|
|
|
||||||
20
tests/nativehtmlgen.nim
Normal file
20
tests/nativehtmlgen.nim
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
## Simple test that shows Karax can also do client-side HTML rendering.
|
||||||
|
|
||||||
|
import "../karax" / [karaxdsl, vdom]
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
{.error: "Use 'nim c' to compile this example".}
|
||||||
|
|
||||||
|
let tab = buildHtml(table):
|
||||||
|
tr:
|
||||||
|
td:
|
||||||
|
text "Cell A"
|
||||||
|
td:
|
||||||
|
text "Cell B"
|
||||||
|
tr:
|
||||||
|
td:
|
||||||
|
text "Cell C"
|
||||||
|
td:
|
||||||
|
text "Cell D"
|
||||||
|
|
||||||
|
echo tab
|
||||||
|
|
@ -1 +1 @@
|
||||||
--path: "../src"
|
--path: "../karax"
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,6 @@ proc main =
|
||||||
exec("nim js examples/mediaplayer/playerapp.nim")
|
exec("nim js examples/mediaplayer/playerapp.nim")
|
||||||
exec("nim js examples/carousel/carousel.nim")
|
exec("nim js examples/carousel/carousel.nim")
|
||||||
exec("nim js -d:nodejs -r tests/difftest.nim")
|
exec("nim js -d:nodejs -r tests/difftest.nim")
|
||||||
|
exec("nim c tests/nativehtmlgen.nim")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue