diff options
Diffstat (limited to '')
| -rw-r--r-- | examples/calculator/index.html | 10 | ||||
| -rw-r--r-- | examples/calculator/style.css | 155 | ||||
| -rw-r--r-- | examples/clock/style.css | 26 | ||||
| -rw-r--r-- | examples/syntax-ideas.css | 17 | ||||
| -rw-r--r-- | src/eval.ts | 52 | ||||
| -rw-r--r-- | src/parser.ts | 2 |
6 files changed, 234 insertions, 28 deletions
diff --git a/examples/calculator/index.html b/examples/calculator/index.html new file mode 100644 index 0000000..94b50e6 --- /dev/null +++ b/examples/calculator/index.html @@ -0,0 +1,10 @@ +<!doctype html> +<html lang="en"> + <head> + <title>Calculator</title> + <link href="./style.css" rel="stylesheet" /> + </head> + <body> + <script async defer src="../../dist/renderer/index.js"></script> + </body> +</html> diff --git a/examples/calculator/style.css b/examples/calculator/style.css new file mode 100644 index 0000000..605c7ed --- /dev/null +++ b/examples/calculator/style.css @@ -0,0 +1,155 @@ +:root { + --cssx-children: main#container; +} + +html, body { + margin: 0; + padding: 0; + background-color: #e2e8f0; + font-size: 16px; +} +body * { + box-sizing: border-box; + font-family: Courier, monospace; +} + +#container { + --num1: ''; + --num2: ''; + --operation: ''; + + max-width: 400px; + margin: 2rem auto; + padding: 1rem; + background-color: #020617; + color: #e2e8f0; + + --cssx-children: #display hr#sep0 #buttons; +} + +#display { + padding: 0 1rem; + line-height: 1.7em; + font-size: 2rem; + height: 3rem; + background-color: #0f172a; + text-align: right; +} +#display::after { + content: var(--num1) var(--operation) var(--num2); +} + +#buttons { + --cssx-children: + h(div#toprow.horizontal, map(), seq( + button#btn-clear, + button#btn-run, + )) + div#buttons-numbers + div#buttons-operators.horizontal + ; +} +#buttons > * { + display: flex; + flex-direction: column; + gap: 20px; +} + +#buttons-operators { + --cssx-children: + instance(button#btn-op, map(--op: "+")) + instance(button#btn-op, map(--op: "-")) + instance(button#btn-op, map(--op: "*")) + instance(button#btn-op, map(--op: "/")) + instance(button#btn-op, map(--op: "!")) + ; +} + +#buttons-numbers { + --cssx-children: + instance(button#btn-num, map(--n: "9")) + instance(button#btn-num, map(--n: "8")) + instance(button#btn-num, map(--n: "7")) + instance(button#btn-num, map(--n: "6")) + instance(button#btn-num, map(--n: "5")) + instance(button#btn-num, map(--n: "4")) + instance(button#btn-num, map(--n: "3")) + instance(button#btn-num, map(--n: "2")) + instance(button#btn-num, map(--n: "1")) + instance(button#btn-num, map(--n: "0")) + ; +} +#buttons-numbers > * { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2px; +} + +[data-instance=btn-num]::after { content: var(--n); } +[data-instance=btn-num] { + width: 100%; + --cssx-on-click: + if(get-var(--operation), + if(equals(get-var(--operation), '!'), + '', + update('container', --num2, string(get-var(--num2), get-var(--n)))), + update('container', --num1, string(get-var(--num1), get-var(--n))), + ); +} + +[data-instance=btn-op]::after { content: var(--op); } +[data-instance=btn-op] { + width: 100%; + --cssx-on-click: update('container', --operation, get-var(--op)); +} + +#btn-run::after { content: '='; } +#btn-run { + --factorial: func(--n: number) + if(lte(get-var(--n), 1), 1, + calc( + get-var(--n) + * call(--factorial, map(--n: calc(get-var(--n) - 1))) + )); + + --cssx-on-click: + update('container', --num1, + if(equals(get-var(--operation), '!'), + call(--factorial, map(--n: get-var(--num1))), + if(get-var(--num2), + js-eval(string(get-var(--num1), get-var(--operation), get-var(--num2))), + get-var(--num1) + ) + ) + ) + update('container', --operation, '') + update('container', --num2, ''); +} + +#btn-clear::after { content: 'clear'; } +#btn-clear { + --cssx-on-click: + update('container', --num1, '') + update('container', --operation, '') + update('container', --num2, ''); +} + +button { + width: 100%; + padding: 0.5rem 0; + border: none; + background-color: #475569; + color: white; + font-weight: bold; + font-size: 1.3rem; + cursor: pointer; +} +button:hover { background-color: #334155; } +button:active { background-color: #64748b; } + +body .horizontal > * { + width: 100%; + display: flex; + justify-content: space-between; + gap: 2px; +} diff --git a/examples/clock/style.css b/examples/clock/style.css index 110d95b..4115723 100644 --- a/examples/clock/style.css +++ b/examples/clock/style.css @@ -15,6 +15,13 @@ )); } +html, body { + margin: 0; + padding: 0; + background-color: #0f172a; + color: #e2e8f0; +} + body * { box-sizing: border-box; } #digital { @@ -38,7 +45,7 @@ body * { box-sizing: border-box; } #digital::after { content: var(--text); } #analog { - border: 1px solid red; + background-color: #1e293b; width: 200px; height: 200px; margin: 1rem auto; @@ -50,25 +57,26 @@ body * { box-sizing: border-box; } --cssx-on-mount: update(--date, call(--get-date)); --cssx-on-update: - update('[data-element=seconds]', --angle, js-eval("360 * new Date().getSeconds() / 60 - 90")) - update('[data-element=minutes]', --angle, js-eval("360 * new Date().getMinutes() / 60 - 90")) - update('[data-element=hours]', --angle, - js-eval("360 * (new Date().getHours() % 12) / 12 - 90 + (30 * new Date().getMinutes() / 60)") - ) + update('[data-element=seconds]', --angle, calc(360 * js-eval("new Date().getSeconds()")/60 - 90)) + update('[data-element=minutes]', --angle, calc(360 * js-eval("new Date().getMinutes()")/60 - 90)) + update('[data-element=hours]', --angle, calc( + 360 * js-eval("new Date().getHours() % 12")/12 - 90 + + (30 * js-eval("new Date().getMinutes()")/60) + )) delay(1s) update(--date, call(--get-date)); } [data-element=seconds].analog-clock-hand { - --color: pink; + --color: #cbd5e1; --size: 70px; } [data-element=minutes].analog-clock-hand { - --color: red; + --color: #991b1b; --size: 60px; } [data-element=hours].analog-clock-hand { - --color: blue; + --color: #4f46e5; --size: 40px; height: 4px; } diff --git a/examples/syntax-ideas.css b/examples/syntax-ideas.css deleted file mode 100644 index bdae5b4..0000000 --- a/examples/syntax-ideas.css +++ /dev/null @@ -1,17 +0,0 @@ -body { - --cssx-children: element-1 element-2 element-3; -} - -#element-1 { - --cssx-on-click: js-eval('alert("clicked")'); -} -#element-2 { - --from-variable: 'is here'; -} -#element-2::after { - content: 'My content: ' var(--from-variable); -} - -#element-3 { - --cssx-on-click: js-eval('alert("clicked")'); -} diff --git a/src/eval.ts b/src/eval.ts index 87bcd6d..84ab52f 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -371,10 +371,59 @@ const getFunctions = ( return EvalValue.Number(result) }, + // TODO: Structural comparison? + equals: async () => + compare( + args[0], + args[1], + actions, + (a, b) => evalValueToString(a) === evalValueToString(b), + ), + + gt: async () => + compare( + args[0], + args[1], + actions, + (a, b) => (evalValueToNumber(a) ?? 0) > (evalValueToNumber(b) ?? 0), + ), + + lt: async () => + compare( + args[0], + args[1], + actions, + (a, b) => (evalValueToNumber(a) ?? 0) < (evalValueToNumber(b) ?? 0), + ), + + gte: async () => + compare( + args[0], + args[1], + actions, + (a, b) => (evalValueToNumber(a) ?? 0) >= (evalValueToNumber(b) ?? 0), + ), + + lte: async () => + compare( + args[0], + args[1], + actions, + (a, b) => (evalValueToNumber(a) ?? 0) <= (evalValueToNumber(b) ?? 0), + ), + _: () => Promise.reject(new Error(`Not implemented: ${name}`)), }) } +export const compare = async ( + a: Expr, + b: Expr, + actions: EvalActions, + cmp: (a: EvalValue, b: EvalValue) => boolean, +) => + EvalValue.Boolean(cmp(await evalExpr(a, actions), await evalExpr(b, actions))) + const evalBinOp = async ( left: Expr, right: Expr, @@ -401,7 +450,8 @@ export const evalCalcExpr = ( }), Parens: ({ expr }) => evalCalcExpr(expr, actions), _: async () => { - if (expr.tag === 'Call' && expr.value.name === 'var') { + // Special expressions to double-evaluate + if (expr.tag === 'Call' && ['var', 'get-var'].includes(expr.value.name)) { const value = await evalExprAsString(expr, actions) try { const pvalue = await evalExpr(parseExpr(value ?? ''), actions) diff --git a/src/parser.ts b/src/parser.ts index 81f7a9e..928fc00 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -116,7 +116,7 @@ const precedence = (op: BinOp) => _: () => -1, }) -const binOpWithFixitySwitchity = (op: BinOp, left: Expr, right: Expr) => +const binOpWithFixitySwitchity = (op: BinOp, left: Expr, right: Expr): Expr => match(right, { BinOp: binOp => { if (precedence(op) >= precedence(binOp.op)) { |
