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.

Reconcile existing tree

garrison 63d83707 694a35e5

+96 -14
+18 -1
demos/todo/todo.tsx
··· 15 15 </div> 16 16 </div> 17 17 ); 18 + 19 + const element2 = ( 20 + <div class="bar"> 21 + <p>Not hello noir</p> 22 + <div> 23 + <span>This is not a demo</span> 24 + </div> 25 + <div> 26 + <span>Foo bar baz</span> 27 + </div> 28 + </div> 29 + ); 18 30 //console.log(JSON.stringify(element, null, 2)); 19 31 20 - const root = Noir.createRoot(document.getElementById('root')); 32 + const rootElement = document.getElementById('root'); 33 + const root = Noir.createRoot(rootElement); 21 34 root.render(element); 35 + 36 + rootElement.addEventListener('click', ev => { 37 + root.render(element2); 38 + });
+23 -5
js/commit.ts
··· 20 20 if (fiber.effect === 'placement') commitElementPlacement(fiber); 21 21 else commitElementUpdate(fiber); 22 22 } 23 + 24 + if (fiber.deletedChildren) { 25 + for (const deleteChild of fiber.deletedChildren) { 26 + commitDeletion(deleteChild); 27 + } 28 + } 29 + } 30 + 31 + function commitDeletion(fiber) { 32 + // TODO: if fiber the does not have a DOM node we need to traverse its children looking for DOM nodes 33 + if (fiber.dom) { 34 + fiber.dom.remove(); 35 + } 23 36 } 24 37 25 38 function commitTextPlacement(fiber) { ··· 33 46 else domParent.appendChild(domNode, domSibling); 34 47 } 35 48 36 - function commitTextUpdate() { 37 - // TODO 38 - throw new Error(); 49 + function commitTextUpdate(fiber) { 50 + const domNode = fiber.dom; 51 + domNode.textContent = fiber.props.nodeValue; 39 52 } 40 53 41 54 function commitElementPlacement(fiber) { ··· 50 63 } 51 64 52 65 function commitElementUpdate(fiber) { 53 - // TODO 54 - throw new Error(); 66 + // TODO: update attributes 55 67 } 56 68 57 69 function findDomParent(forFiber) { ··· 64 76 } 65 77 66 78 function findNextExistingDomSibling(forFiber) { 79 + // TODO: traverse sibling children looking for DOM nodes 80 + let fiber = forFiber.sibling; 81 + while (fiber) { 82 + if (fiber.dom) return fiber.dom; 83 + fiber = fiber.sibling; 84 + } 67 85 return null; 68 86 } 69 87
+48 -8
js/fiber.ts
··· 9 9 HostTextFiber, 10 10 } = FiberTag; 11 11 12 - export function makeFiber(tag) { 12 + let nextFiberId = 0; 13 + 14 + export function makeFiber(tag, id) { 13 15 return { 16 + id: id || nextFiberId++, 17 + version: 0, 18 + 14 19 tag: tag, 15 20 type: null, 16 21 props: null, ··· 25 30 child: null, 26 31 sibling: null, 27 32 33 + deletedChildren: null, 34 + 28 35 alternate: null, 29 36 }; 30 37 } 31 38 39 + function cloneFiber(fromFiber) { 40 + const fiber = fromFiber.alternate || makeFiber(fromFiber.tag, fromFiber.id); 41 + fiber.version = fromFiber.version + 1; 42 + 43 + fiber.dom = fromFiber.dom; 44 + 45 + // parent and sibling will be set by reconciliation 46 + // TODO: maybe setting to null here is a waste of time 47 + fiber.parent = null; 48 + fiber.child = fromFiber.child; 49 + fiber.sibling = null; 50 + 51 + fiber.alternate = fromFiber; 52 + 53 + return fiber; 54 + } 55 + 32 56 export function reconcileChildren(parentFiber, childElementOrElements) { 33 57 if (Array.isArray(childElementOrElements)) { 34 58 return reconcileArray(parentFiber, childElementOrElements); ··· 39 63 40 64 function reconcileArray(parentFiber, elements) { 41 65 const oldFiberLookup = buildLookup(parentFiber.child); 66 + 67 + parentFiber.deletedChildren = []; 42 68 43 69 let firstNewChild = null; 44 70 let prevChild; ··· 52 78 53 79 newFiber.parent = parentFiber; 54 80 newFiber.index = i; 55 - newFiber.alternate = oldFiber; 56 - if (oldFiber) { 81 + if (newFiber.alternate) { 57 82 const oldIndex = oldFiber.index; 58 83 if (oldIndex < lastKeptIndex) { 59 84 newFiber.effect = 'placement'; ··· 65 90 else { 66 91 newFiber.effect = 'placement'; 67 92 } 93 + if (oldFiber && oldFiber.effect === 'delete') { 94 + parentFiber.deletedChildren.push(oldFiber); 95 + } 68 96 69 97 if (prevChild) prevChild.sibling = newFiber; 70 98 else firstNewChild = newFiber; 71 99 prevChild = newFiber; 72 100 } 73 101 102 + oldFiberLookup.forEach(deadFiber => { 103 + deadFiber.effect = 'delete'; 104 + parentFiber.deletedChildren.push(deadFiber); 105 + }); 106 + 74 107 return firstNewChild; 75 108 } 76 109 ··· 85 118 } 86 119 87 120 function updateTextNode(oldFiber, element) { 121 + const nodeValue = element.toString(); 122 + 88 123 if (oldFiber === null || oldFiber.tag !== HostTextFiber) { 124 + if (oldFiber) oldFiber.effect = 'delete'; 89 125 const newFiber = makeFiber(HostTextFiber); 90 - const nodeValue = element.toString(); 91 126 newFiber.props = { nodeValue }; 92 127 return newFiber; 93 128 } 94 129 else { 95 - // TODO 96 - throw new Error(); 130 + const newFiber = cloneFiber(oldFiber); 131 + newFiber.props = { nodeValue }; 132 + return newFiber; 97 133 } 98 134 } 99 135 100 136 function updateElement(oldFiber, element, index) { 137 + // TODO: need to guard against different tag types here 101 138 if (oldFiber === null || oldFiber.type !== element.type) { 139 + if (oldFiber) oldFiber.effect = 'delete'; 102 140 const newFiber = makeFiber(HostFiber); 103 141 newFiber.type = element.type; 104 142 newFiber.props = element.props; 105 143 return newFiber; 106 144 } 107 145 else { 108 - // TODO 109 - throw new Error(); 146 + const newFiber = cloneFiber(oldFiber); 147 + newFiber.type = element.type; 148 + newFiber.props = element.props; 149 + return newFiber; 110 150 } 111 151 } 112 152
+7
priv/static/todo.html
··· 2 2 <html> 3 3 <head> 4 4 <script defer src="/assets/js/todo.js"></script> 5 + <style> 6 + body { 7 + padding: 32px; 8 + margin: 0 auto; 9 + width: 800px; 10 + } 11 + </style> 5 12 </head> 6 13 <body> 7 14 <div id="root" />