diff options
| author | Akshay Nair <phenax5@gmail.com> | 2023-08-13 21:12:14 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2023-08-13 21:12:14 +0530 |
| commit | 6411d58b48682c65165a1e717a9bf837737887f3 (patch) | |
| tree | a56db95e45f0a68c2536cde827bd3c3c4a023088 | |
| parent | 78550c0d1c7037b17bdaa9413351b759b20772c0 (diff) | |
| download | css-everything-6411d58b48682c65165a1e717a9bf837737887f3.tar.gz css-everything-6411d58b48682c65165a1e717a9bf837737887f3.zip | |
fix: get-var hack
| -rw-r--r-- | TODO.norg | 1 | ||||
| -rw-r--r-- | examples/todo-list/index.html | 10 | ||||
| -rw-r--r-- | examples/todo-list/style.css | 106 | ||||
| -rw-r--r-- | src/eval.ts | 19 | ||||
| -rw-r--r-- | src/index.ts | 42 |
5 files changed, 140 insertions, 38 deletions
@@ -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 ( |
