a small incremental UI library for the web
javascript web ui
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add CSS parser

garrison be513964 51a1ce1e

+171 -2
+12 -2
demos/todo/todo.tsx
··· 1 1 import * as Noir from '../../js/noir.ts'; 2 - import { useState, useEffect, useFiber } from '../../js/hooks.ts'; 2 + import { useState, useEffect, useStyle, useFiber } from '../../js/hooks.ts'; 3 3 4 4 function App(props) { 5 5 return ( ··· 13 13 const [items, setItems] = useState(['item 1', 'item 2', 'item 3']); 14 14 const [newItemText, setNewItemText] = useState(''); 15 15 16 + useStyle(` 17 + .container > .foo, .bar, [foo="bar"] { 18 + background-color: red; 19 + border: 1px solid green; 20 + } 21 + 22 + .foo { color: 'red'; } 23 + .bar { color: 'green'; } 24 + `); 25 + 16 26 return ( 17 - <div> 27 + <div class="container"> 18 28 <div> 19 29 <input 20 30 onInput={ev => setNewItemText(ev.target.value)}
+154
js/css.ts
··· 1 + import { assert } from './utils'; 2 + 3 + export function parseCSS(input) { 4 + const stylesheet = parseStylesheet(input); 5 + console.log(JSON.stringify(stylesheet, null, 2)); 6 + return stylesheet; 7 + } 8 + 9 + function parseStylesheet(input) { 10 + const state = { 11 + input: input, 12 + pos: 0, 13 + }; 14 + 15 + skipWhitepsace(state); 16 + 17 + const rulesets = []; 18 + while (state.pos < state.input.length) { 19 + const ruleset = parseRuleset(state); 20 + rulesets.push(ruleset); 21 + skipWhitepsace(state); 22 + } 23 + 24 + return { 25 + type: 'stylesheet', 26 + rulesets: rulesets, 27 + }; 28 + } 29 + 30 + function parseRuleset(state) { 31 + const {input} = state; 32 + 33 + const selectorList = parseSelectorList(state); 34 + assert(input.charAt(state.pos) === '{'); 35 + state.pos++; 36 + 37 + const body = parseRulesetBody(state); 38 + assert(state.input.charAt(state.pos) === '}'); 39 + state.pos++; 40 + 41 + return { 42 + type: 'ruleset', 43 + selectorList: selectorList, 44 + body: body, 45 + }; 46 + } 47 + 48 + function parseRulesetBody(state) { 49 + const {input} = state; 50 + const start = state.pos; 51 + while (true) { 52 + refuteEof(state); 53 + 54 + if (input.charAt(state.pos) === '}') break; 55 + state.pos++; 56 + } 57 + 58 + return { 59 + type: 'ruleset_body', 60 + text: input.substring(start, state.pos), 61 + }; 62 + } 63 + 64 + function parseSelectorList(state) { 65 + const {input} = state; 66 + const selectors = []; 67 + 68 + while (true) { 69 + refuteEof(state); 70 + 71 + skipWhitepsace(state); 72 + const complex = parseComplexSelector(state); 73 + selectors.push(complex); 74 + 75 + skipWhitepsace(state); 76 + const char = input.charAt(state.pos); 77 + if (char === '{') break; 78 + 79 + assert(char === ','); 80 + state.pos++; 81 + } 82 + 83 + return { 84 + type: 'selector_list', 85 + selectors: selectors, 86 + } 87 + } 88 + 89 + function parseComplexSelector(state) { 90 + const {input} = state; 91 + const selectors = []; 92 + while (true) { 93 + refuteEof(state); 94 + 95 + const simple = parseCompoundSelector(state); 96 + selectors.push(simple); 97 + 98 + skipWhitepsace(state); 99 + const char = input.charAt(state.pos); 100 + if (char === '{' || char === ',') break; 101 + 102 + if (isCombinator(char)) { 103 + selectors.push({type: 'combinator', text: char}); 104 + state.pos++; 105 + skipWhitepsace(state); 106 + } 107 + } 108 + 109 + return { 110 + type: 'complex_selector', 111 + content: selectors, 112 + }; 113 + } 114 + 115 + function isCombinator(char) { 116 + return char === '+' || char === '>' || char === '~' || char === '|'; 117 + } 118 + 119 + function parseCompoundSelector(state) { 120 + const {input} = state; 121 + const start = state.pos; 122 + 123 + while (true) { 124 + refuteEof(state); 125 + 126 + const char = input.charAt(state.pos); 127 + if (isWhitespace(char) || char === ',') break; 128 + 129 + state.pos++; 130 + } 131 + 132 + assert((state.pos - start) > 0); 133 + return { 134 + type: 'simple_selector', 135 + text: input.slice(start, state.pos), 136 + }; 137 + } 138 + 139 + function skipWhitepsace(state) { 140 + const {input} = state; 141 + while (state.pos < input.length) { 142 + const char = input.charAt(state.pos); 143 + if (char !== '\n' && char !== ' ') return; 144 + state.pos++; 145 + } 146 + } 147 + 148 + function isWhitespace(char) { 149 + return char === '\n' || char === ' '; 150 + } 151 + 152 + function refuteEof(state) { 153 + assert(state.pos < state.input.length); 154 + }
+5
js/hooks.ts
··· 1 1 import { queueFiberTree } from './render.ts'; 2 2 import { getCurrentFiber } from './current.ts'; 3 3 import { pushSideEffect } from './effects.ts'; 4 + import { parseCSS } from './css.ts'; 4 5 5 6 export const Hook = { 6 7 State: 'state', ··· 73 74 fiber.hooks[pointer + 1] = newCleanup; 74 75 }); 75 76 } 77 + } 78 + 79 + export function useStyle(text) { 80 + const ast = parseCSS(text); 76 81 } 77 82 78 83 export function cleanUpEffects(fiber) {