summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2023-08-13 21:12:14 +0530
committerAkshay Nair <phenax5@gmail.com>2023-08-13 21:12:14 +0530
commit6411d58b48682c65165a1e717a9bf837737887f3 (patch)
treea56db95e45f0a68c2536cde827bd3c3c4a023088
parent78550c0d1c7037b17bdaa9413351b759b20772c0 (diff)
downloadcss-everything-6411d58b48682c65165a1e717a9bf837737887f3.tar.gz
css-everything-6411d58b48682c65165a1e717a9bf837737887f3.zip
fix: get-var hack
Diffstat (limited to '')
-rw-r--r--TODO.norg1
-rw-r--r--examples/todo-list/index.html10
-rw-r--r--examples/todo-list/style.css106
-rw-r--r--src/eval.ts19
-rw-r--r--src/index.ts42
5 files changed, 140 insertions, 38 deletions
diff --git a/TODO.norg b/TODO.norg
index 2dc3b78..bac7d16 100644
--- a/TODO.norg
+++ b/TODO.norg
@@ -24,6 +24,7 @@
- ( ) string concatenation
- ( ) `request` error handling
- ( ) keyboard events
+ - ( ) Code cleanup
- ( ) Evaluate `calc`
- ( ) Additional events
- ( ) Improve error messages
diff --git a/examples/todo-list/index.html b/examples/todo-list/index.html
new file mode 100644
index 0000000..064ef2b
--- /dev/null
+++ b/examples/todo-list/index.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <title>Task destroyer</title>
+ <link href="./style.css" rel="stylesheet" />
+ </head>
+ <body>
+ <script async defer src="../../dist/renderer/index.js"></script>
+ </body>
+</html>
diff --git a/examples/todo-list/style.css b/examples/todo-list/style.css
index 7533210..5316d41 100644
--- a/examples/todo-list/style.css
+++ b/examples/todo-list/style.css
@@ -1,33 +1,97 @@
-body {
- --cssx-children: todo-container;
-}
+:root {
+ --color-gray: #cccccc;
+ --color-accent: #5180e9;
+
+ font-size: 16px;
+ color: #555;
-#todo-container {
- --cssx-children: todo-input todo-list;
- --todo-list: list();
+ --cssx-children: main#container;
}
-#todo-input {
- --cssx-children: input-field submit-btn;
+#container {
+ max-width: 600px;
+ margin: 2rem auto;
+ border: 1px solid var(--color-gray);
+ border-radius: 5px;
+ overflow: hidden;
+
+ --cssx-children: form#task-input-form #task-list;
}
-#submit-btn {
- --cssx-on-click: update(
- todo-container,
- --todo-list,
- list-append(var(--todo-list), tuple(get-attr(input-field, value), false))
- );
+#task-input-form {
+ display: flex;
+ width: 100%;
+
+ /* prettier-ignore */
+ --cssx-on-submit:
+ prevent-default()
+ add-children(
+ task-list,
+ instance(div#task-item, map(--text: attr(text-input, 'value')))
+ )
+ set-attr(text-input, value, '')
+ ;
+
+ /* prettier-ignore */
+ --cssx-on-mount:
+ add-children(
+ task-list,
+ instance(div#task-item, map(--text: "Buy lemons"))
+ )
+ add-children(
+ task-list,
+ instance(div#task-item, map(--text: "Make lemonaide"))
+ )
+ add-children(
+ task-list,
+ instance(div#task-item, map(--text: "Kill all the non-believers"))
+ )
+ ;
+
+ /* prettier-ignore */
+ --cssx-children:
+ input#text-input[placeholder="Eg: Buy Milk"]
+ button#create-task-btn[type=submit]
+ ;
}
-#submit-btn::after {
- content: 'Submit';
+
+#text-input {
+ display: block;
+ width: 100%;
+ padding: 0.7rem 1rem;
+ font-size: 1rem;
+ border: none;
+ border-bottom: 1px solid var(--color-gray);
}
-#todo-list {
- --cssx-iter-children: iter(var(--todo-list), --todo-item, todo-item);
+#create-task-btn {
+ padding: 0 2rem;
+ background-color: var(--color-accent);
+ color: white;
+ font-weight: bold;
+ border: none;
+ font-size: 1.2rem;
+
+ --cssx-text: '+';
}
-#todo-item {
+
+[data-instance=task-item] {
+ --text: "default text";
+ --checked: "false";
+
+ padding: 1rem;
+ cursor: pointer;
+
+ --cssx-on-click: update(--checked, if(get-var(--checked), "false", "true"));
}
-#todo-item::after {
- content: var(--todo-item);
+[data-instance=task-item]::after {
+ content: "[" var(--checked) "] " var(--text);
}
+
+[data-instance=task-item]:not(:first-child) {
+ content: "";
+ display: block;
+ border-top: 1px solid var(--color-gray);
+}
+
diff --git a/src/eval.ts b/src/eval.ts
index 1a55364..445cb8f 100644
--- a/src/eval.ts
+++ b/src/eval.ts
@@ -53,8 +53,14 @@ export const evalExpr = async (
_: async _ => undefined,
})
-const getFunctions = (name: string, args: Expr[], actions: EvalActions) =>
- matchString<Promise<EvalValue>>(name, {
+const getFunctions = (name: string, args: Expr[], actions: EvalActions) => {
+ const getVariable = async () => {
+ const varName = await evalExpr(args[0], actions)
+ const defaultValue = args[1] && (await evalExpr(args[1], actions))
+ return varName && (actions.getVariable(varName) ?? defaultValue)
+ }
+
+ return matchString<Promise<EvalValue>>(name, {
'add-class': async () => {
const id = await evalExpr(args[0], actions)
const classes = await evalExpr(args[1], actions)
@@ -96,11 +102,9 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) =>
}
},
- var: async () => {
- const varName = await evalExpr(args[0], actions)
- const defaultValue = args[1] && (await evalExpr(args[1], actions))
- return varName && (actions.getVariable(varName) ?? defaultValue)
- },
+ var: getVariable,
+ 'get-var': getVariable,
+
update: async () => {
const [id, name, value] =
args.length >= 3
@@ -153,6 +157,7 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) =>
_: () => Promise.reject(new Error('not supposed to be here')),
})
+}
export const evalArgs = (
args: Array<Expr>,
diff --git a/src/index.ts b/src/index.ts
index 47e65fe..765f676 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -5,14 +5,18 @@ import {
expressionsToDeclrs,
} from './declarations'
import { parse } from './parser'
-import { match } from './utils/adt'
+import { match, matchString } from './utils/adt'
+
+const CSSX_ON_UPDATE_EVENT = 'cssx--update'
+const CSSX_ON_MOUNT_EVENT = 'cssx--mount'
const UNSET_PROPERTY_VALUE = '<unset>'
const EVENT_HANDLERS = {
click: '--cssx-on-click',
load: '--cssx-on-load',
- mount: '--cssx-on-mount',
submit: '--cssx-on-submit',
+ [CSSX_ON_MOUNT_EVENT]: '--cssx-on-mount',
+ [CSSX_ON_UPDATE_EVENT]: '--cssx-on-update',
}
const PROPERTIES = [
@@ -32,6 +36,9 @@ export const injectStyles = () => {
$style.textContent = `.cssx-layer {
${properties.map(p => `${p}: ${UNSET_PROPERTY_VALUE};`).join(' ')}
+ display: inherit;
+ width: inherit;
+ height: inherit;
}`
document.body.appendChild($style)
@@ -85,7 +92,13 @@ const getEvalActions = (
updateVariable: async (targetId, varName, value) => {
const $el = targetId ? document.getElementById(targetId) : $element
if ($el) {
+ const prevValue = getPropertyValue($el, varName)
;($el as any).style.setProperty(varName, JSON.stringify(value))
+
+ if (JSON.stringify(value) !== prevValue) {
+ const detail = { name: varName, value, prevValue }
+ $el.dispatchEvent(new CustomEvent(CSSX_ON_UPDATE_EVENT, { detail }))
+ }
}
},
setAttribute: async (id, name, value) => {
@@ -141,11 +154,20 @@ export const handleEvents = async (
}
}
- if (eventType === 'mount') {
- if (isNewElement) setTimeout(eventHandler)
- } else {
- ;($element as any)[`on${eventType}`] = eventHandler
- }
+ matchString(eventType, {
+ [CSSX_ON_UPDATE_EVENT]: () => {
+ if (!$element.hasAttribute('data-hooked')) {
+ $element.addEventListener(eventType, eventHandler)
+ $element.setAttribute('data-hooked', 'true')
+ }
+ },
+ [CSSX_ON_MOUNT_EVENT]: () => {
+ if (isNewElement) setTimeout(eventHandler)
+ },
+ _: () => {
+ ;($element as any)[`on${eventType}`] = eventHandler
+ },
+ })
}
}
}
@@ -173,7 +195,7 @@ const declarationToElement = (
}
for (const [key, value] of declaration.properties) {
- ;($child as HTMLElement)?.style.setProperty(key, value)
+ ;($child as HTMLElement)?.style.setProperty(key, JSON.stringify(value))
}
return { node: $child, isNewElement }
@@ -188,6 +210,8 @@ const createLayer = async (
$parent?.querySelector(`:scope > .${LAYER_CLASS}`) ??
Object.assign(document.createElement('div'), { className: LAYER_CLASS })
+ if (!$childrenRoot.parentNode) $parent.appendChild($childrenRoot)
+
for (const declaration of declarations) {
const { node: $child, isNewElement } = declarationToElement(
declaration,
@@ -196,8 +220,6 @@ const createLayer = async (
$childrenRoot.appendChild($child)
await manageElement($child, isNewElement)
}
-
- if (!$childrenRoot.parentNode) $parent.appendChild($childrenRoot)
}
export const manageElement = async (