diff --git a/task_2_ts/ts/rendering/list-renderer.ts b/task_2_ts/ts/rendering/list-renderer.ts index 659e048..2b8c7d5 100644 --- a/task_2_ts/ts/rendering/list-renderer.ts +++ b/task_2_ts/ts/rendering/list-renderer.ts @@ -26,9 +26,9 @@ const renderer = ( data: T, template: RenderTempl if ( template.children.length === 0 ) { if ( template.attribute ) { - parent.innerText = String( data[ template.attribute ] ); + parent.textContent = String( data[ template.attribute ] ); } else { - parent.innerText = String( data ); + parent.textContent = String( data ); } } diff --git a/task_2_ts/ts/rendering/list.ts b/task_2_ts/ts/rendering/list.ts index 5f2cbb8..2dc4752 100644 --- a/task_2_ts/ts/rendering/list.ts +++ b/task_2_ts/ts/rendering/list.ts @@ -30,7 +30,7 @@ export const listRef = ( parent: HTMLElement, data: T[], name: string, templa // Yes, I know, really bad performance, etc, but it's not needed for any other use case // here, other than a full replace of the data (no dynamic updates) list = data; - console.log( data ); + parent.textContent = ''; // Render the list based on template for ( let i = 0; i < data.length; i++ ) { @@ -92,6 +92,7 @@ export const listRef = ( parent: HTMLElement, data: T[], name: string, templa if ( !evaluation && rendered[ index ] ) { // can use ! here, as semantics of program tell us that this index will exist nodes[ index ]!.remove(); + rendered[ index ] = false; } else if ( evaluation && !rendered[ index ] ) { currentIndexInChildrenList++; parent.insertBefore( nodes[ index ]!, children[ currentIndexInChildrenList ]! ); diff --git a/task_2_ts/ts/rendering/primitives.ts b/task_2_ts/ts/rendering/primitives.ts index e02e5d2..3619a79 100644 --- a/task_2_ts/ts/rendering/primitives.ts +++ b/task_2_ts/ts/rendering/primitives.ts @@ -2,6 +2,17 @@ import { Ref } from './rendering'; +interface ConditionalElement { + 'element': HTMLElement; + 'predicate': ( value: T ) => boolean; +} + +interface ConditionalClass { + 'element': HTMLElement, + 'onTrue': string, + 'onFalse': string +} + /** * Responsive data (similar behaviour as in Vue.js) * @template T - The data type you wish to use (as long as you don't want it to be a list) @@ -10,34 +21,44 @@ import { */ export const ref = ( elements: HTMLElement[], data: T ): Ref => { let value: T = data; - let conditionalElements: HTMLElement[] = []; + const conditionalElements: ConditionalElement[] = []; const onChangeFunctions: ( () => Promise )[] = []; - const conditionalClasses: { - 'element': HTMLElement, - 'onTrue': string, - 'onFalse': string - }[] = []; + const conditionalClasses: ConditionalClass[] = []; const boundElements: HTMLInputElement[] = []; - // ─────────────────────────────────────────────────────────────────── + + /** + * Bind to a further element (DOM text is updated for it) + * @param element - The element to add + */ + const addAdditionalElement = ( element: HTMLElement ) => { + elements.push( element ); + }; + + /** + * @returns The value the ref currently holds + */ const get = (): T => { return value; }; - // ─────────────────────────────────────────────────────────────────── + + /** + * @param data - The new value of the + */ const set = ( data: T ): void => { value = data; // Update normal ref elements elements.forEach( el => { - el.innerText = String( data ); + el.textContent = String( data ); } ); // Update conditional elements conditionalElements.forEach( el => { // convert to boolean (explicitly) - el.hidden = Boolean( data ); + el.element.hidden = !el.predicate( data ); } ); conditionalClasses.forEach( el => { @@ -54,6 +75,7 @@ export const ref = ( elements: HTMLElement[], data: T ): Ref => { } }; + /** * Bind to input change of an HTMLInputElement (two way bind) * @param element - The element to bind to (i.e. add a two-way bind to) @@ -63,17 +85,25 @@ export const ref = ( elements: HTMLElement[], data: T ): Ref => { element.addEventListener( 'change', () => { set( castFunction( element.value ) ); } ); + element.value = String( value ); boundElements.push( element ); }; + /** * Add elements to be rendered conditionally on this ref. Treats type as booleanish - * @param elements - The elements that are rendered consistently + * @param element - The element that will be affected by predicate + * @param predicate - The predicate to evaluate when value is changed */ - const setConditionalElements = ( elements: HTMLElement[] ): void => { - conditionalElements = elements; + const addConditionalElementBind = ( element: HTMLElement, predicate: ( value: T ) => boolean ): void => { + conditionalElements.push( { + 'element': element, + 'predicate': predicate + } ); + element.hidden = !predicate( value ); }; + /** * @param element - The element to do the operation on * @param onTrue - The classes (as strings) to set if true(ish) @@ -107,7 +137,8 @@ export const ref = ( elements: HTMLElement[], data: T ): Ref => { return { set, get, - setConditionalElements, + addAdditionalElement, + addConditionalElementBind, addConditionalClasses, bind, onChange diff --git a/task_2_ts/ts/rendering/rendering.d.ts b/task_2_ts/ts/rendering/rendering.d.ts index c6fe86f..c2dde49 100644 --- a/task_2_ts/ts/rendering/rendering.d.ts +++ b/task_2_ts/ts/rendering/rendering.d.ts @@ -1,7 +1,8 @@ export interface Ref { 'set': ( data: T ) => void; 'get': () => T; - 'setConditionalElements': ( elements: HTMLElement[] ) => void; + 'addAdditionalElement': ( elements: HTMLElement, predicate: ( value: T ) => boolean ) => void; + 'addConditionalElementBind': ( elements: HTMLElement, predicate: ( value: T ) => boolean ) => void; 'addConditionalClasses': ( element: HTMLElement, onTrue: string, onFalse: string ) => void; 'bind': ( element: HTMLInputElement, castFunction: ( val: string ) => T ) => void; 'onChange': ( callback: () => void ) => void;