Skip to content

Commit

Permalink
Support function-level states
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinresol committed May 28, 2020
1 parent 9276be1 commit c8a2e6f
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 2 deletions.
2 changes: 2 additions & 0 deletions demo.hxml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@

-lib coconut.storybook
-lib coconut.vdom

-D analyzer-optimize
1 change: 1 addition & 0 deletions extraParams.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--macro coconut.storybook.Setup.setup()
3 changes: 2 additions & 1 deletion haxe_libraries/coconut.storybook.hxml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
-lib coconut.ui
-cp ${SCOPE_DIR}/./src
-cp ${SCOPE_DIR}/../coconut.storybook/src
-D coconut.storybook=0.0.0
${SCOPE_DIR}/../coconut.storybook/extraParams.hxml
--macro Sys.println("haxe_libraries/coconut.storybook.hxml:3: [Warning] Using dev version of library coconut.storybook")
6 changes: 6 additions & 0 deletions src/coconut/storybook/Component.macro.hx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package coconut.storybook;

import haxe.macro.Context;
import haxe.macro.Expr;

using tink.MacroApi;

class Component {
public static function build() {
var builder = new ClassBuilder();

if (!builder.target.meta.has(':tink'))
builder.target.meta.add(':tink', [], Context.currentPos());

if (!builder.target.meta.has(':storybook'))
builder.target.meta.add(':storybook', [], Context.currentPos());

return null;
}

Expand Down
103 changes: 103 additions & 0 deletions src/coconut/storybook/Setup.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package coconut.storybook;

import haxe.macro.Context;
import haxe.macro.Expr;
import tink.SyntaxHub;

using StringTools;
using tink.MacroApi;

class Setup {
public static function setup() {
var debug = false;
SyntaxHub.classLevel.after('tink.lang.Sugar', builder -> {
if (!builder.target.meta.has(':tink'))
return false;

for (field in builder)
switch [field.kind, field.metaNamed(':story'), field.metaNamed(':state')] {
case [FFun(func), stories, v] if (stories.length > 0):
// wrap with Isolated
function subst(e:Expr)
return switch e {
case macro return $ret:
macro return coconut.ui.Isolated.fromHxx({}, {children: $ret});
case e:
e;
}
func.expr = func.expr.map(subst);

// add states
for (states in v)
for (state in states.params)
switch state.expr {
case EVars(vars):
for (v in vars) {
if (v.expr == null)
state.pos.error('@:state var ${v.name} requires a initializer');
if (v.type == null)
state.pos.error('@:state var ${v.name} requires a type hint');

var name = v.name;
var alias = getAlias(name);
var ct = v.type;

var original = func.expr;
func.expr = macro {
var $name = new tink.state.State<$ct>(${v.expr});
var $alias = $i{name};
${func.expr}
}
}

case _:
state.pos.error('Only supports EVars expressions');
}

case _:
// skip
}

return true;
});

SyntaxHub.exprLevel.inward.after(_ -> true, {
appliesTo: builder -> builder.target.meta.has(':storybook'),
apply: (e:Expr) -> {
function transform(name:String, original:Expr, transformed:Expr) {
return switch Context.getLocalTVars()[name] {
case null:
original;
case {t: _.getID() => 'tink.state.State'}:
transformed;
case _:
original;
}
}

switch e.expr {
case EConst(CIdent(name)) if (!isAlias(name)):
transform(name, e, macro $i{getAlias(name)}.value);

case EBinop(OpAssign, macro $i{name}, rhs) if (!isAlias(name)):
transform(name, e, macro $i{getAlias(name)}.set($rhs));

case EBinop(OpAssignOp(binop), macro $i{name}, rhs) if (!isAlias(name)):
var rhs = EBinop(binop, macro $i{getAlias(name)}.value, rhs).at(e.pos);
transform(name, e, macro $i{getAlias(name)}.set($rhs));

case _:
e;
}
}
});
}

inline static function getAlias(name:String) {
return '__alias_${name}';
}

inline static function isAlias(name:String) {
return name.startsWith('__alias_');
}
}
6 changes: 6 additions & 0 deletions src/coconut/storybook/Storybook.macro.hx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ using tink.MacroApi;

class Storybook {
public static macro function add(exprs:Array<Expr>):Expr {
switch exprs {
case [{expr: EArrayDecl(values)}]:
exprs = values;
case _:
}

var ret = [];

for (expr in exprs) {
Expand Down
22 changes: 21 additions & 1 deletion tests/Demo.hx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import coconut.ui.*;

class Demo {
static function main() {
Storybook.add(new Button(), new foo.Bar());
Storybook.add([
// @formatter:off
new Button(),
new foo.Bar(),
// @formatter:on
]);
}
}

Expand All @@ -31,6 +36,14 @@ class Button extends Component {
</button>
';

@:story
@:state(var value:Int = 0)
function withState() '
<button onclick=${value += 1}>
Clicked ${value} time(s)
</button>
';

function wrap(f:()->RenderResult) '
<div style=${{backgroundColor: 'black'}}>${f()}</div>
';
Expand All @@ -43,3 +56,10 @@ extern class Knobs {
static function boolean(name:String, value:Bool):Bool;
static function text(name:String, value:String):String;
}

class Isolated extends View {
@:attribute var children:Children;

function render()
'<>${...children}</>';
}

0 comments on commit c8a2e6f

Please sign in to comment.