summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2023-08-11 17:13:33 +0530
committerAkshay Nair <phenax5@gmail.com>2023-08-11 17:13:33 +0530
commit67748db74f73343b054ee0af1763e376a5470416 (patch)
tree39b8df64ea1d657ef107fcdfed2da8ce66063589
parentdfd4da6ca2d06c9d05d5c7e297304316da211dd4 (diff)
downloadcss-everything-67748db74f73343b054ee0af1763e376a5470416.tar.gz
css-everything-67748db74f73343b054ee0af1763e376a5470416.zip
feat: adds more functions for eval
-rw-r--r--TODO.norg9
-rw-r--r--examples/api/content.css26
-rw-r--r--examples/api/style.css10
-rw-r--r--src/eval.ts58
-rw-r--r--src/index.ts32
-rw-r--r--tests/eval.spec.ts31
6 files changed, 132 insertions, 34 deletions
diff --git a/TODO.norg b/TODO.norg
index 6469cb9..6ec8df2 100644
--- a/TODO.norg
+++ b/TODO.norg
@@ -1,9 +1,10 @@
* Tasks
- (x) Hydrate existing elements instead of re-creating
- - ( ) `load-cssx` functions
+ - (x) `load-cssx` functions
+ - (x) `get-variable`
+ - (x) `update-variable`
- ( ) Use css units for `delay` function
- - ( ) `get-variable`
- - ( ) `update-variable`
+ - ( ) Improve error messages
+ - ( ) Specify node type + attributes
- ( ) Evaluate `calc`?
- ( ) `list` & `tuple` data structures?
- - ( ) Non div elements
diff --git a/examples/api/content.css b/examples/api/content.css
new file mode 100644
index 0000000..5463ba2
--- /dev/null
+++ b/examples/api/content.css
@@ -0,0 +1,26 @@
+
+#output-container-content {
+ padding: 0.5rem;
+ margin-top: 1rem;
+ border: 1px solid #888;
+
+ --cssx-children: counter-text btn-increment;
+ --count: '0';
+}
+#output-container-content::after {
+ content: "Loaded from external stylesheet";
+}
+
+#counter-text {}
+#counter-text::after { content: var(--count) }
+
+#btn-increment {
+ display: inline-block;
+ border: 1px solid gray;
+ padding: 0.3rem 0.6rem;
+ font-size: 0.9rem;
+ cursor: pointer;
+
+ --cssx-on-click: update(output-container-content, --count, '99');
+}
+#btn-increment::after { content: "Increment" }
diff --git a/examples/api/style.css b/examples/api/style.css
index 213d9e2..81eada1 100644
--- a/examples/api/style.css
+++ b/examples/api/style.css
@@ -4,17 +4,18 @@ body {
#load-btn {
display: inline-block;
- border: 1px solid gray;
+ background: #5180e9;
+ color: #000;
padding: 0.5rem 1rem;
cursor: pointer;
--cssx-on-click:
add-class(output-container, 'loading')
add-class(load-btn, 'loading')
- delay('2000')
+ load-cssx(output-container-content, './content.css')
remove-class(output-container, 'loading')
- add-class(output-container, 'loaded')
remove-class(load-btn, 'loading')
+ delay('50')
js-eval('alert("Loaded page")')
;
}
@@ -30,7 +31,4 @@ body {
#output-container.loading::after {
content: "Loading...";
}
-#output-container.loaded::after {
- content: "This content is loaded my guy";
-}
diff --git a/src/eval.ts b/src/eval.ts
index 76eaa21..03391c2 100644
--- a/src/eval.ts
+++ b/src/eval.ts
@@ -1,46 +1,67 @@
import { Expr } from "./parser";
import { match, matchString } from "./utils/adt";
-export type Dependencies = {
+export type EvalActions = {
addClass(id: string, classes: string): Promise<void>
removeClass(id: string, classes: string): Promise<void>
delay(num: number): Promise<void>
jsEval(js: string): Promise<any>
- // loadCssx(id: string, url: string): Promise<string>
- // getVarable(name: string, def?: string): Promise<string>
- // updateVariable(id: string, varName: string, value: string): Promise<void>
+ loadCssx(id: string, url: string): Promise<string>
+ getVariable(name: string): Promise<string | undefined>
+ updateVariable(id: string, varName: string, value: string): Promise<void>
// calculate ??
}
-export const evalExpr = async (expr: Expr, deps: Dependencies): Promise<string | undefined> =>
- match<Promise<string | undefined>, Expr>(expr, {
+type EvalValue = string | undefined | void
+
+export const evalExpr = async (expr: Expr, actions: EvalActions): Promise<EvalValue> =>
+ match<Promise<EvalValue>, Expr>(expr, {
Call: async ({ name, args }) => {
- await matchString(name, {
+ return matchString<Promise<EvalValue>, string>(name, {
'add-class': async () => {
- const id = await evalExpr(args[0], deps)
- const classes = await evalExpr(args[1], deps)
+ const id = await evalExpr(args[0], actions)
+ const classes = await evalExpr(args[1], actions)
if (id && classes) {
- await deps.addClass(id, classes)
+ await actions.addClass(id, classes)
}
},
'remove-class': async () => {
- const id = await evalExpr(args[0], deps)
- const classes = await evalExpr(args[1], deps)
+ const id = await evalExpr(args[0], actions)
+ const classes = await evalExpr(args[1], actions)
if (id && classes) {
- await deps.removeClass(id, classes)
+ await actions.removeClass(id, classes)
}
},
'delay': async () => {
- const num = await evalExpr(args[0], deps)
- num && await deps.delay(parseInt(num, 10))
+ const num = await evalExpr(args[0], actions)
+ num && await actions.delay(parseInt(num, 10))
},
'js-eval': async () => {
- const js = await evalExpr(args[0], deps)
- js && await deps.jsEval(js)
+ const js = await evalExpr(args[0], actions)
+ js && await actions.jsEval(js)
+ },
+ 'load-cssx': async () => {
+ const id = await evalExpr(args[0], actions)
+ const url = await evalExpr(args[1], actions)
+ if (id && url) {
+ await actions.loadCssx(id, url)
+ }
+ },
+ 'var': async () => {
+ const varName = await evalExpr(args[0], actions)
+ const defaultValue = await evalExpr(args[1], actions)
+ return varName && (actions.getVariable(varName) ?? defaultValue)
+ },
+ 'update': async () => {
+ const id = await evalExpr(args[0], actions)
+ const varName = await evalExpr(args[1], actions)
+ const value = await evalExpr(args[2], actions)
+ if (id && varName && value) {
+ actions.updateVariable(id, varName, value)
+ }
},
_: () => Promise.reject(new Error('not supposed to be here')),
})
- return undefined
},
LiteralString: async s => s,
Identifier: async s => s,
@@ -48,3 +69,4 @@ export const evalExpr = async (expr: Expr, deps: Dependencies): Promise<string |
_: async _ => undefined,
})
+
diff --git a/src/index.ts b/src/index.ts
index 4987e65..9e9443c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,4 @@
-import { Dependencies, evalExpr } from './eval'
+import { EvalActions, evalExpr } from './eval'
import { parse } from './parser'
const UNSET_PROPERTY_VALUE = '<unset>'
@@ -33,12 +33,37 @@ const getChildrenIds = ($element: Element) => {
return value.split(/(\s*,\s*)|\s+/g).filter(Boolean)
}
-const evalDeps = (_el: Element): Dependencies => ({
+const getEvalActions = ($element: Element): 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}`)
+ }
+ }
+ 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))
+ }
+ },
})
const handleEvents = async ($element: Element) => {
@@ -50,7 +75,7 @@ const handleEvents = async ($element: Element) => {
console.log(`Triggered event: ${event}`)
const exprs = parse(handlerExpr)
for (const expr of exprs) {
- await evalExpr(expr, evalDeps($element))
+ await evalExpr(expr, getEvalActions($element))
}
}
}
@@ -73,6 +98,7 @@ const manageElement = async ($element: Element) => {
$element.appendChild($childrenRoot)
for (const id of childrenIds) {
+ // TODO: Allow adding node types other than div
const $child =
$childrenRoot.querySelector(`:scope > #${id}`) ??
Object.assign(document.createElement('div'), { id })
diff --git a/tests/eval.spec.ts b/tests/eval.spec.ts
index 63062e3..8b7424f 100644
--- a/tests/eval.spec.ts
+++ b/tests/eval.spec.ts
@@ -1,10 +1,15 @@
-import { Dependencies, evalExpr } from '../src/eval'
+import { EvalActions, evalExpr } from '../src/eval'
import { Expr } from '../src/parser'
describe('eval', () => {
- const deps: Dependencies = {
+ const deps: EvalActions = {
addClass: jest.fn(),
removeClass: jest.fn(),
+ delay: jest.fn(),
+ jsEval: jest.fn(),
+ loadCssx: jest.fn(),
+ getVariable: jest.fn(),
+ updateVariable: jest.fn(),
}
it('should add classes', async () => {
@@ -17,7 +22,7 @@ describe('eval', () => {
expect(deps.addClass).toHaveBeenCalledWith('element-id', 'class-name')
})
- it('should add classes', async () => {
+ it('should remove classes', async () => {
await evalExpr(Expr.Call({
name: 'remove-class',
args: [ Expr.Identifier('element-id'), Expr.LiteralString('class-name') ],
@@ -26,4 +31,24 @@ describe('eval', () => {
expect(deps.removeClass).toHaveBeenCalledTimes(1)
expect(deps.removeClass).toHaveBeenCalledWith('element-id', 'class-name')
})
+
+ it('should add a delay', async () => {
+ await evalExpr(Expr.Call({
+ name: 'delay',
+ args: [ Expr.LiteralString('200') ],
+ }), deps)
+
+ expect(deps.delay).toHaveBeenCalledTimes(1)
+ expect(deps.delay).toHaveBeenCalledWith(200)
+ })
+
+ it('should get variable', async () => {
+ await evalExpr(Expr.Call({
+ name: 'var',
+ args: [ Expr.LiteralString('--my-var'), Expr.LiteralString('def value') ],
+ }), deps)
+
+ expect(deps.getVariable).toHaveBeenCalledTimes(1)
+ expect(deps.getVariable).toHaveBeenCalledWith('--my-var')
+ })
})