summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--TODO.norg3
-rw-r--r--examples/form/signup.css11
-rw-r--r--examples/todo-list/style.css8
-rw-r--r--src/declarations.ts141
-rw-r--r--src/index.ts13
5 files changed, 77 insertions, 99 deletions
diff --git a/TODO.norg b/TODO.norg
index 348b06f..588994e 100644
--- a/TODO.norg
+++ b/TODO.norg
@@ -8,13 +8,14 @@
- (x) Scoped catch on try
- (x) `do` expression
- (x) `let` expression
+ - ( ) `h` declarations
- ( ) `has-class`
- ( ) `add-class` & `remove-class` should use self if id is not specified?
- ( ) Update `--cssx-text` on update
* Later
- ( ) keyboard events
- - ( ) Code cleanup
+ - ( ) Tail recursion optimization
- ( ) Additional events
- ( ) Improve parser error messages
- ( ) Improve eval error messages
diff --git a/examples/form/signup.css b/examples/form/signup.css
index 2335334..5dfb8f6 100644
--- a/examples/form/signup.css
+++ b/examples/form/signup.css
@@ -39,7 +39,16 @@
remove-class(form, 'submitting')
;
- --cssx-children: input#input-email input#input-password #actions #message;
+ --cssx-children:
+ input#input-email
+ input#input-password
+ #actions
+ h(#message[data-attr="wiow"], map(--wow: "wow"),
+ div#wow
+ div#yay
+ h(div#yay, map(--wow: "wow"))
+ )
+ ;
}
#form.submitted #message::after {
display: block;
diff --git a/examples/todo-list/style.css b/examples/todo-list/style.css
index cda7003..66ec5f0 100644
--- a/examples/todo-list/style.css
+++ b/examples/todo-list/style.css
@@ -18,14 +18,6 @@ body * { box-sizing: border-box; }
border-radius: 5px;
overflow: hidden;
- /* --my-func: */
- /* js-eval(get-var(--js)) */
- /* delay(1s) */
- /* call(--my-func, map(--js: get-var(--js))) */
- /* ; */
- /**/
- /* --cssx-on-mount: call(--my-func, map(--js: "console.log(`yay`)")); */
-
--cssx-children: form#task-input-form #task-list;
}
diff --git a/src/declarations.ts b/src/declarations.ts
index c0966fa..a82f42f 100644
--- a/src/declarations.ts
+++ b/src/declarations.ts
@@ -1,37 +1,14 @@
-import { EvalActions, evalExprAsString } from './eval'
+import { EvalActions, EvalValue, evalExpr } from './eval'
import { Expr, Selector, SelectorComp, parseDeclarations } from './parser'
import { match, matchString } from './utils/adt'
export interface Declaration {
selector: Selector
- properties: Map<string, Expr>
+ properties: Map<string, EvalValue>
+ children: Array<Declaration>
isInstance: boolean
}
-export interface DeclarationEval {
- selector: Selector
- properties: Array<readonly [string, string]>
-}
-
-export const evaluateDeclaration = async (
- { selector, properties }: Declaration,
- actions: EvalActions,
-): Promise<DeclarationEval> => {
- if (properties.size === 0) return { selector, properties: [] }
-
- const props = await Promise.all(
- [...properties.entries()].map(async ([key, expr]) => {
- // Ignore errors?
- const result = await evalExprAsString(expr, actions).catch((e: any) =>
- console.warn(e),
- )
- return [key, result ?? ''] as const
- }),
- )
-
- return { selector, properties: props }
-}
-
const instanceCountMap = new Map<string, number>()
const getUniqueInstanceId = (id: string) => {
const instanceCount = instanceCountMap.get(id) ?? 0
@@ -39,78 +16,76 @@ const getUniqueInstanceId = (id: string) => {
return `${id}--index-${instanceCount}`
}
-export const toDeclaration = (expr: Expr): Declaration | undefined => {
- let selector: Selector | undefined
- const properties: Map<string, Expr> = new Map()
- let isInstance = false
+export const toDeclaration =
+ (actions: EvalActions) =>
+ async (expr: Expr): Promise<Declaration | undefined> => {
+ let selector: Selector | undefined
+ const properties: Map<string, EvalValue> = new Map()
+ const children: Array<Declaration> = []
+ let isInstance = false
- match(expr, {
- Selector: sel => {
- selector = sel
- },
- Call: ({ name, args }) => {
- matchString(name, {
- instance: () => {
- isInstance = true
- const [sel, map] = args
+ await match(expr, {
+ Selector: async sel => {
+ selector = sel
+ },
+ Call: async ({ name, args }) => {
+ return matchString(name, {
+ // h: () => {
+ //
+ // },
+ instance: async () => {
+ isInstance = true
+ const [sel, map] = args
- // Selector
- match(sel, {
- Selector: sel => {
- selector = sel
- },
- _: _ => {},
- })
+ // Selector
+ match(sel, {
+ Selector: sel => {
+ selector = sel
+ },
+ _: _ => {},
+ })
- // TODO: Refactor with eval
- match(map, {
- Call: ({ name, args }) => {
- if (name !== 'map') return
- for (const arg of args) {
- match(arg, {
- Pair: ({ key, value }) => properties.set(key, value),
- _: _ => {},
- })
- }
- },
- })
- },
- _: () => {
- throw new Error(`weird function in cssx-chi9ldren: ${name}`)
- },
- })
- },
- _: () => {},
- })
+ const props = await evalExpr(map, actions)
+ match(props, {
+ Map: props => {
+ for (const [key, value] of Object.entries(props)) {
+ properties.set(key, value)
+ }
+ },
+ _: _ => {},
+ })
+ },
+ _: async () => {
+ throw new Error(`weird function in cssx-chi9ldren: ${name}`)
+ },
+ })
+ },
+ _: async () => {},
+ })
- if (!selector) return undefined
+ if (!selector) return undefined
- if (isInstance) {
- const baseId = selector.id
- selector.id = getUniqueInstanceId(selector.id)
- selector.selectors.push(SelectorComp.Attr(['data-instance', baseId]))
- }
+ if (isInstance) {
+ const baseId = selector.id
+ selector.id = getUniqueInstanceId(selector.id)
+ selector.selectors.push(SelectorComp.Attr(['data-instance', baseId]))
+ }
- return { selector, properties, isInstance }
-}
+ return { selector, properties, children, isInstance }
+ }
export const expressionsToDeclrs = async (
exprs: Array<Expr>,
actions: EvalActions,
-): Promise<Array<DeclarationEval>> => {
- const declrs = await Promise.all(
- exprs
- .map(toDeclaration)
- .filter(declr => !!declr)
- .map(declr => declr && evaluateDeclaration(declr, actions)),
- )
- return declrs.filter(declr => !!declr) as Array<DeclarationEval>
+): Promise<Array<Declaration>> => {
+ const declrs = await Promise.all(exprs.map(toDeclaration(actions)))
+ return declrs.filter(declr => !!declr) as Array<Declaration>
}
export const extractDeclaration = async (
input: string,
actions: EvalActions,
-): Promise<Array<DeclarationEval>> => {
+): Promise<Array<Declaration>> => {
const exprs = parseDeclarations(input)
return expressionsToDeclrs(exprs, actions)
}
diff --git a/src/index.ts b/src/index.ts
index e5803d2..69d1fea 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,8 +7,8 @@ import {
} from './eval'
import {
extractDeclaration,
- DeclarationEval,
expressionsToDeclrs,
+ Declaration,
} from './declarations'
import { Expr, parse } from './parser'
import { match, matchString } from './utils/adt'
@@ -68,7 +68,7 @@ export const getPropertyValue = ($element: HTMLElement, prop: string) => {
export const getDeclarations = (
$element: HTMLElement,
actions: EvalActions,
-): Promise<Array<DeclarationEval>> => {
+): Promise<Array<Declaration>> => {
const value = getPropertyValue($element, PROPERTIES.CHILDREN)
return extractDeclaration(value, actions)
}
@@ -248,7 +248,7 @@ export const handleEvents = async (
}
const declarationToElement = (
- declaration: DeclarationEval,
+ declaration: Declaration,
$parent?: HTMLElement,
): { node: HTMLElement; isNewElement: boolean } => {
const { tag, id, selectors } = declaration.selector
@@ -270,15 +270,16 @@ const declarationToElement = (
})
}
- for (const [key, value] of declaration.properties) {
- $child?.style.setProperty(key, JSON.stringify(value))
+ for (const [key, evalValue] of declaration.properties) {
+ const value = evalValueToString(evalValue)
+ $child?.style.setProperty(key, JSON.stringify(value || ''))
}
return { node: $child, isNewElement }
}
const createLayer = async (
- declarations: Array<DeclarationEval>,
+ declarations: Array<Declaration>,
$parent: HTMLElement,
) => {
const $childrenRoot =