diff options
| author | Akshay Nair <phenax5@gmail.com> | 2023-08-20 12:42:06 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2023-08-20 12:42:06 +0530 |
| commit | af65d13038dcdeaf93c5c718cbc74c20120c6a22 (patch) | |
| tree | e1d36b54a9c8db4660d61dde03dbcf8d50cde76e | |
| parent | f61677bbc3ae32cc460014cffe4d9ae9264291c5 (diff) | |
| download | css-everything-af65d13038dcdeaf93c5c718cbc74c20120c6a22.tar.gz css-everything-af65d13038dcdeaf93c5c718cbc74c20120c6a22.zip | |
feat: implements function call and adds support for recursion
Diffstat (limited to '')
| -rw-r--r-- | examples/todo-list/style.css | 8 | ||||
| -rw-r--r-- | src/declarations.ts | 1 | ||||
| -rw-r--r-- | src/eval.ts | 54 | ||||
| -rw-r--r-- | src/index.ts | 51 | ||||
| -rw-r--r-- | tests/eval.spec.ts | 3 |
5 files changed, 107 insertions, 10 deletions
diff --git a/examples/todo-list/style.css b/examples/todo-list/style.css index 66ec5f0..cda7003 100644 --- a/examples/todo-list/style.css +++ b/examples/todo-list/style.css @@ -18,6 +18,14 @@ 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 5c2f9a3..c0966fa 100644 --- a/src/declarations.ts +++ b/src/declarations.ts @@ -62,6 +62,7 @@ export const toDeclaration = (expr: Expr): Declaration | undefined => { _: _ => {}, }) + // TODO: Refactor with eval match(map, { Call: ({ name, args }) => { if (name !== 'map') return diff --git a/src/eval.ts b/src/eval.ts index e421d7a..8112353 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -1,4 +1,4 @@ -import { CSSUnit, Expr } from './parser' +import { CSSUnit, Expr, parse } from './parser' import { Enum, constructors, match, matchString } from './utils/adt' export interface EvalActions { @@ -36,6 +36,10 @@ export interface EvalActions { method: string, args: (string | undefined)[], ): Promise<void> + evaluateInScope( + exprs: Expr[], + properties: Record<string, EvalValue>, + ): Promise<EvalValue> // calculate ?? } @@ -46,6 +50,7 @@ export type EvalValue = Enum<{ Lazy: Expr[] Void: never VarIdentifier: string + Map: { [key in string]: EvalValue } // Object: Record<any, any> }> export const EvalValue = constructors<EvalValue>() @@ -75,7 +80,7 @@ export const evalExpr = async ( _: async _ => EvalValue.Void(), }) -const evalValueToString = (val: EvalValue): string | undefined => +export const evalValueToString = (val: EvalValue): string | undefined => match<string | undefined, EvalValue>(val, { String: s => s.replace(/(^'|")|('|"$)/g, ''), Boolean: b => `${b}`, @@ -245,8 +250,53 @@ const getFunctions = ( return EvalValue.Void() }, + map: async () => { + const values = await Promise.all( + args.map(async mapExpr => + match<Promise<undefined | [string, EvalValue]>, Expr>(mapExpr, { + Pair: async ({ key, value }) => [ + key, + await evalExpr(value, actions), + ], + _: async () => undefined, + }), + ), + ) + + return EvalValue.Map(Object.fromEntries(values.filter(Boolean) as any)) + }, + func: async () => EvalValue.Lazy(args), + call: async () => { + const varId = match<string | undefined, EvalValue>( + await evalExpr(args[0], actions), + { + VarIdentifier: id => id, + _: () => undefined, + }, + ) + + const propMapExpr = await evalExpr(args[1], actions) + const properties = match<Record<string, EvalValue>, EvalValue>( + propMapExpr, + { + Map: m => m, + _: () => ({}), + }, + ) + + if (varId) { + const prop = await actions.getVariable(varId) + if (prop) { + const exprs = parse(prop) + return actions.evaluateInScope(exprs, properties) + } + } + + return EvalValue.Void() + }, + _: () => Promise.reject(new Error(`Not implemented: ${name}`)), }) } diff --git a/src/index.ts b/src/index.ts index 52f3a1b..e7e42cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,16 @@ -import { EvalActions, evalExpr, evalExprAsString } from './eval' +import { + EvalActions, + EvalValue, + evalExpr, + evalExprAsString, + evalValueToString, +} from './eval' import { extractDeclaration, DeclarationEval, expressionsToDeclrs, } from './declarations' -import { parse } from './parser' +import { Expr, parse } from './parser' import { match, matchString } from './utils/adt' const CSSX_ON_UPDATE_EVENT = 'cssx--update' @@ -88,8 +94,10 @@ const getElement = ( const getEvalActions = ( $element: HTMLElement, - { event = null, pure = false }: { event?: any; pure?: boolean }, + ctx: { event?: any; pure?: boolean }, ): EvalActions => { + const { event = null, pure = false } = ctx + const actions: EvalActions = { addClass: async (id, cls) => getElement(id, $element)?.classList.add(cls), removeClass: async (id, cls) => @@ -172,10 +180,39 @@ const getEvalActions = ( const $el = id ? getElement(id, $element) : $element ;($el as any)[method].call($el, args) }, + + evaluateInScope: async (exprs, properties) => { + const node = document.createElement('div') + node.style.display = 'none' + + for (const [key, evalVal] of Object.entries(properties)) { + const value = evalValueToString(evalVal) + console.log(key, evalVal, value) + value && node.style.setProperty(key, value) + } + $element.appendChild(node) + + const result = await evalExprInScope(exprs, getEvalActions(node, ctx)) + + // node.parentNode?.removeChild(node) + + return result + }, } return actions } +const evalExprInScope = async ( + exprs: Expr[], + actions: EvalActions, +): Promise<EvalValue> => { + let lastVal = EvalValue.Void() + for (const expr of exprs) { + lastVal = await evalExpr(expr, actions) + } + return lastVal +} + export const handleEvents = async ( $element: HTMLElement, isNewElement: boolean = false, @@ -185,10 +222,10 @@ export const handleEvents = async ( if (handlerExpr) { const eventHandler = async (event: any) => { - const exprs = parse(handlerExpr) - for (const expr of exprs) { - await evalExpr(expr, getEvalActions($element, { event })) - } + await evalExprInScope( + parse(handlerExpr), + getEvalActions($element, { event }), + ) } matchString(eventType, { diff --git a/tests/eval.spec.ts b/tests/eval.spec.ts index d61a470..2b226f7 100644 --- a/tests/eval.spec.ts +++ b/tests/eval.spec.ts @@ -1,5 +1,5 @@ import { EvalActions, EvalValue, evalExpr } from '../src/eval' -import { Expr, exprParser, parseExpr } from '../src/parser' +import { Expr, parseExpr } from '../src/parser' describe('eval', () => { const deps: EvalActions = { @@ -18,6 +18,7 @@ describe('eval', () => { addChildren: jest.fn(), removeElement: jest.fn(), callMethod: jest.fn(), + evaluateInScope: jest.fn(), } fdescribe('function/call', () => { |
