summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.norg6
-rw-r--r--examples/counter/style.css1
-rw-r--r--src/declarations.ts5
-rw-r--r--src/eval.ts2
-rw-r--r--src/index.ts158
-rw-r--r--tests/fixtures/todo-app/index.html23
-rw-r--r--tests/signup.spec.ts9
-rw-r--r--tests/todo-app.spec.ts7
8 files changed, 110 insertions, 101 deletions
diff --git a/TODO.norg b/TODO.norg
index cd74df4..5148115 100644
--- a/TODO.norg
+++ b/TODO.norg
@@ -15,9 +15,9 @@
- (x) `selector` parsing
- (x) `map` data structure
- (x) component system (with variables. `instance(button#my-btn)`)
- - ( ) instance access
- - ( ) More complex selector support for cssx-children
- - ( ) `add-element` & `remove-element`
+ - (x) More complex selector support for cssx-children
+ - (x) `add-element` & `remove-element`
+ - ( ) access an instance of component
- ( ) string concatenation
- ( ) `request` error handling
- ( ) keyboard events
diff --git a/examples/counter/style.css b/examples/counter/style.css
index b60785f..b193f23 100644
--- a/examples/counter/style.css
+++ b/examples/counter/style.css
@@ -1,6 +1,5 @@
body {
--cssx-children: container todo-container;
- --cssx-on-load: js(console.log('what have we done?!'));
}
#container {
diff --git a/src/declarations.ts b/src/declarations.ts
index 78c9801..17306bd 100644
--- a/src/declarations.ts
+++ b/src/declarations.ts
@@ -50,12 +50,15 @@ export const toDeclaration = (expr: Expr): Declaration | undefined => {
instance: () => {
isInstance = true
const [sel, map] = args
+
+ // Selector
match(sel, {
Selector: sel => {
selector = sel
},
_: _ => {},
})
+
match(map, {
Call: ({ name, args }) => {
if (name !== 'map') return
@@ -90,7 +93,7 @@ export const toDeclaration = (expr: Expr): Declaration | undefined => {
export const expressionsToDeclrs = async (
exprs: Array<Expr>,
actions: EvalActions,
-) => {
+): Promise<Array<DeclarationEval>> => {
const declrs = await Promise.all(
exprs
.map(toDeclaration)
diff --git a/src/eval.ts b/src/eval.ts
index 3d6734f..79eeb79 100644
--- a/src/eval.ts
+++ b/src/eval.ts
@@ -85,7 +85,7 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) =>
var: async () => {
const varName = await evalExpr(args[0], actions)
- const defaultValue = await evalExpr(args[1], actions)
+ const defaultValue = args[1] && (await evalExpr(args[1], actions))
return varName && (actions.getVariable(varName) ?? defaultValue)
},
update: async () => {
diff --git a/src/index.ts b/src/index.ts
index 81e5b88..c90588b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -51,72 +51,75 @@ export const getDeclarations = (
return extractDeclaration(value, actions)
}
-const getEvalActions = ($element: Element, event: any): EvalActions => ({
- addClass: async (id, cls) => document.getElementById(id)?.classList.add(cls),
- removeClass: async (id, cls) =>
- document.getElementById(id)?.classList.remove(cls),
- delay: delay => new Promise(res => setTimeout(res, delay)),
- jsEval: async js => (0, eval)(js),
- loadCssx: async (id, url) =>
- new Promise((resolve, reject) => {
- const $link = Object.assign(document.createElement('link'), {
- href: url,
- rel: 'stylesheet',
- })
- $link.onload = () => {
- const $el = document.getElementById(id)
- // NOTE: Maybe create and append to body if no root?
- if ($el) {
- manageElement($el)
- resolve(id)
- } else {
- console.error(`[CSSX] Unable to find root for ${id}`)
- reject(`[CSSX] Unable to find root for ${id}`)
+const getEvalActions = (
+ $element: Element,
+ { event = null, pure = false }: { event?: any; pure?: boolean },
+): EvalActions => {
+ const actions: EvalActions = {
+ addClass: async (id, cls) =>
+ document.getElementById(id)?.classList.add(cls),
+ removeClass: async (id, cls) =>
+ document.getElementById(id)?.classList.remove(cls),
+ delay: delay => new Promise(res => setTimeout(res, delay)),
+ jsEval: async js => (0, eval)(js),
+ loadCssx: async (id, url) =>
+ new Promise((resolve, reject) => {
+ const $link = Object.assign(document.createElement('link'), {
+ href: url,
+ rel: 'stylesheet',
+ })
+ $link.onload = () => {
+ const $el = document.getElementById(id)
+ if ($el) {
+ manageElement($el)
+ resolve(id)
+ } else {
+ console.error(`[CSSX] Unable to find root for ${id}`)
+ reject(`[CSSX] Unable to find root for ${id}`)
+ }
}
+ document.body.appendChild($link)
+ }),
+ getVariable: async varName => getPropertyValue($element, varName),
+ updateVariable: async (targetId, varName, value) => {
+ const $el = document.getElementById(targetId)
+ if ($el) {
+ $el.style.setProperty(varName, JSON.stringify(value))
}
- document.body.appendChild($link)
- }),
- getVariable: async varName => getPropertyValue($element, varName),
- updateVariable: async (targetId, varName, value) => {
- const $el = document.getElementById(targetId)
- if ($el) {
- $el.style.setProperty(varName, JSON.stringify(value))
- }
- },
- setAttribute: async (id, name, value) => {
- const $el = id ? document.getElementById(id) : $element
- if (value) {
- $el?.setAttribute(name, value)
- } else {
- $el?.removeAttribute(name)
- }
- },
- getAttribute: async (id, name) => {
- const $el = id ? document.getElementById(id) : $element
- return $el?.getAttribute(name) ?? undefined
- },
- withEvent: async fn => fn(event),
- getFormData: async () =>
- $element.nodeName === 'FORM'
- ? new FormData($element as HTMLFormElement)
- : undefined,
- sendRequest: async ({ url, method, data }) => {
- await fetch(url, { method, body: data })
- // TODO: Handle response?
- },
- addChildren: async (id, children) => {
- const $el = document.getElementById(id)
- const declarations = await expressionsToDeclrs(
- children,
- getEvalActions($element, event),
- )
- $el && createLayer(declarations, $el)
- },
- removeElement: async id => {
- const $el = id ? document.getElementById(id) : $element
- $el?.parentNode?.removeChild($el)
- },
-})
+ },
+ setAttribute: async (id, name, value) => {
+ const $el = id ? document.getElementById(id) : $element
+ if (value) {
+ $el?.setAttribute(name, value)
+ } else {
+ $el?.removeAttribute(name)
+ }
+ },
+ getAttribute: async (id, name) => {
+ const $el = id ? document.getElementById(id) : $element
+ return $el?.getAttribute(name) ?? undefined
+ },
+ withEvent: async fn => event && fn(event),
+ getFormData: async () =>
+ $element.nodeName === 'FORM'
+ ? new FormData($element as HTMLFormElement)
+ : undefined,
+ sendRequest: async ({ url, method, data }) => {
+ await fetch(url, { method, body: data })
+ // TODO: Handle response?
+ },
+ addChildren: async (id, children) => {
+ const $el = document.getElementById(id)
+ const declarations = await expressionsToDeclrs(children, actions)
+ $el && createLayer(declarations, $el)
+ },
+ removeElement: async id => {
+ const $el = id ? document.getElementById(id) : $element
+ $el?.parentNode?.removeChild($el)
+ },
+ }
+ return actions
+}
export const handleEvents = async (
$element: Element,
@@ -129,7 +132,7 @@ export const handleEvents = async (
const eventHandler = async (event: any) => {
const exprs = parse(handlerExpr)
for (const expr of exprs) {
- await evalExpr(expr, getEvalActions($element, event))
+ await evalExpr(expr, getEvalActions($element, { event }))
}
}
@@ -150,6 +153,7 @@ const declarationToElement = (
const tagName = tag || 'div'
let $child = $parent?.querySelector(`:scope > #${id}`)
+ const isNewElement = !$child
if (!$child) {
$child = Object.assign(document.createElement(tagName), { id })
}
@@ -163,7 +167,11 @@ const declarationToElement = (
})
}
- return { node: $child, isNewElement: !$child }
+ for (const [key, value] of declaration.properties) {
+ ;($child as HTMLElement)?.style.setProperty(key, value)
+ }
+
+ return { node: $child, isNewElement }
}
const createLayer = async (
@@ -193,16 +201,24 @@ export const manageElement = async (
) => {
await handleEvents($element, isNewElement)
+ const actions = getEvalActions($element, { pure: true })
+
const text = getPropertyValue($element, '--cssx-text')
- if (text) $element.textContent = text
+ if (text) {
+ const exprs = parse(text)
+ try {
+ $element.textContent =
+ (exprs[0] ? await evalExpr(exprs[0], actions) : text) ?? text
+ } catch (e) {
+ console.log(e, exprs)
+ $element.textContent = text
+ }
+ }
const html = getPropertyValue($element, '--cssx-disgustingly-set-innerhtml')
if (html) $element.innerHTML = html.replace(/(^'|")|('|"$)/g, '')
- const declarations = await getDeclarations(
- $element,
- getEvalActions($element, null),
- )
+ const declarations = await getDeclarations($element, actions)
if (declarations.length > 0) {
await createLayer(declarations, $element)
}
diff --git a/tests/fixtures/todo-app/index.html b/tests/fixtures/todo-app/index.html
index 66a9798..55996f3 100644
--- a/tests/fixtures/todo-app/index.html
+++ b/tests/fixtures/todo-app/index.html
@@ -10,7 +10,11 @@
#task-input-form {
--cssx-on-submit:
prevent-default()
- js-eval('console.log("todo: implement add new task")')
+ add-children(task-list,
+ instance(li#task-item, map(
+ --text: "Hello world",
+ ))
+ )
;
--cssx-children:
@@ -21,24 +25,13 @@
#text-input {}
#create-task-btn {
- --cssx-text: Submit;
+ --cssx-text: "Submit";
}
- #task-list {
- --cssx-children:
- instance(li#task-item, map(
- --text: "hello world",
- --checked: 0
- ))
- instance(li#task-item, map(
- --text: "coolio stuff",
- --checked: 0
- ))
- ;
- }
+ #task-list { }
[data-instance="task-item"] {
- --text: "default text";
+ --text: default text;
--checked: 0;
--cssx-text: var(--text);
diff --git a/tests/signup.spec.ts b/tests/signup.spec.ts
index 1317049..4764d7f 100644
--- a/tests/signup.spec.ts
+++ b/tests/signup.spec.ts
@@ -3,6 +3,7 @@ import {
waitFor,
getByText,
getByTestId,
+ prettyDOM,
} from '@testing-library/dom'
import '@testing-library/jest-dom'
import { delay, loadHTMLFixture } from './util'
@@ -33,9 +34,9 @@ describe('signup example', () => {
beforeEach(async () => {
const $showFormBtn = document.getElementById('show-form-btn')!
fireEvent.click($showFormBtn)
- await waitFor(() => expect($showFormBtn).not.toBeVisible())
- const $form = document.getElementById('signup-form')!
- expect($form).toBeVisible()
+ await waitFor(() =>
+ expect(document.getElementById('signup-form')).toBeVisible(),
+ )
await delay(100) // Wait for mounting
})
@@ -48,7 +49,7 @@ describe('signup example', () => {
const $password = getByTestId<HTMLInputElement>(document.body, 'password')
$password.value = 'password'
- await delay(2000)
+ await delay(100)
// Submit form
const $submitBtn = getByText(document.body, 'Submit')
diff --git a/tests/todo-app.spec.ts b/tests/todo-app.spec.ts
index 1e0cd72..2bdcdee 100644
--- a/tests/todo-app.spec.ts
+++ b/tests/todo-app.spec.ts
@@ -23,11 +23,8 @@ describe('todo-app example', () => {
await delay(100)
- console.log(prettyDOM(document.body))
- console.log()
- console.log()
- console.log()
- console.log()
+ console.log(prettyDOM(document.getElementById('task-list')!))
+ console.log('-------------------')
})
})
})