summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2024-01-21 19:34:49 +0530
committerAkshay Nair <phenax5@gmail.com>2024-01-21 19:34:49 +0530
commite4db8ebdd6a36df0b3d76504d18d8f183fe46ba9 (patch)
tree807071e55e97d8169158c896822cc57c023c9af1
parentc9075367a178644d12a179919aa07616938b7315 (diff)
downloadcss-everything-e4db8ebdd6a36df0b3d76504d18d8f183fe46ba9.tar.gz
css-everything-e4db8ebdd6a36df0b3d76504d18d8f183fe46ba9.zip
feat: adds example for calculator
-rw-r--r--examples/calculator/index.html10
-rw-r--r--examples/calculator/style.css155
-rw-r--r--examples/clock/style.css26
-rw-r--r--examples/syntax-ideas.css17
-rw-r--r--src/eval.ts52
-rw-r--r--src/parser.ts2
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)) {