Framework rendering fixes

This commit is contained in:
2025-10-20 15:09:16 +02:00
parent 3e0f2fb781
commit b23ef80a5f
4 changed files with 51 additions and 18 deletions

View File

@@ -26,9 +26,9 @@ const renderer = <T extends StringIndexedObject>( data: T, template: RenderTempl
if ( template.children.length === 0 ) { if ( template.children.length === 0 ) {
if ( template.attribute ) { if ( template.attribute ) {
parent.innerText = String( data[ template.attribute ] ); parent.textContent = String( data[ template.attribute ] );
} else { } else {
parent.innerText = String( data ); parent.textContent = String( data );
} }
} }

View File

@@ -30,7 +30,7 @@ export const listRef = <T>( parent: HTMLElement, data: T[], name: string, templa
// Yes, I know, really bad performance, etc, but it's not needed for any other use case // 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) // here, other than a full replace of the data (no dynamic updates)
list = data; list = data;
console.log( data ); parent.textContent = '';
// Render the list based on template // Render the list based on template
for ( let i = 0; i < data.length; i++ ) { for ( let i = 0; i < data.length; i++ ) {
@@ -92,6 +92,7 @@ export const listRef = <T>( parent: HTMLElement, data: T[], name: string, templa
if ( !evaluation && rendered[ index ] ) { if ( !evaluation && rendered[ index ] ) {
// can use ! here, as semantics of program tell us that this index will exist // can use ! here, as semantics of program tell us that this index will exist
nodes[ index ]!.remove(); nodes[ index ]!.remove();
rendered[ index ] = false;
} else if ( evaluation && !rendered[ index ] ) { } else if ( evaluation && !rendered[ index ] ) {
currentIndexInChildrenList++; currentIndexInChildrenList++;
parent.insertBefore( nodes[ index ]!, children[ currentIndexInChildrenList ]! ); parent.insertBefore( nodes[ index ]!, children[ currentIndexInChildrenList ]! );

View File

@@ -2,6 +2,17 @@ import {
Ref Ref
} from './rendering'; } from './rendering';
interface ConditionalElement<T> {
'element': HTMLElement;
'predicate': ( value: T ) => boolean;
}
interface ConditionalClass {
'element': HTMLElement,
'onTrue': string,
'onFalse': string
}
/** /**
* Responsive data (similar behaviour as in Vue.js) * 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) * @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 = <T>( elements: HTMLElement[], data: T ): Ref<T> => { export const ref = <T>( elements: HTMLElement[], data: T ): Ref<T> => {
let value: T = data; let value: T = data;
let conditionalElements: HTMLElement[] = [];
const conditionalElements: ConditionalElement<T>[] = [];
const onChangeFunctions: ( () => Promise<void> )[] = []; const onChangeFunctions: ( () => Promise<void> )[] = [];
const conditionalClasses: { const conditionalClasses: ConditionalClass[] = [];
'element': HTMLElement,
'onTrue': string,
'onFalse': string
}[] = [];
const boundElements: HTMLInputElement[] = []; 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 => { const get = (): T => {
return value; return value;
}; };
// ───────────────────────────────────────────────────────────────────
/**
* @param data - The new value of the
*/
const set = ( data: T ): void => { const set = ( data: T ): void => {
value = data; value = data;
// Update normal ref elements // Update normal ref elements
elements.forEach( el => { elements.forEach( el => {
el.innerText = String( data ); el.textContent = String( data );
} ); } );
// Update conditional elements // Update conditional elements
conditionalElements.forEach( el => { conditionalElements.forEach( el => {
// convert to boolean (explicitly) // convert to boolean (explicitly)
el.hidden = Boolean( data ); el.element.hidden = !el.predicate( data );
} ); } );
conditionalClasses.forEach( el => { conditionalClasses.forEach( el => {
@@ -54,6 +75,7 @@ export const ref = <T>( elements: HTMLElement[], data: T ): Ref<T> => {
} }
}; };
/** /**
* Bind to input change of an HTMLInputElement (two way bind) * 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) * @param element - The element to bind to (i.e. add a two-way bind to)
@@ -63,17 +85,25 @@ export const ref = <T>( elements: HTMLElement[], data: T ): Ref<T> => {
element.addEventListener( 'change', () => { element.addEventListener( 'change', () => {
set( castFunction( element.value ) ); set( castFunction( element.value ) );
} ); } );
element.value = String( value );
boundElements.push( element ); boundElements.push( element );
}; };
/** /**
* Add elements to be rendered conditionally on this ref. Treats type as booleanish * 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 => { const addConditionalElementBind = ( element: HTMLElement, predicate: ( value: T ) => boolean ): void => {
conditionalElements = elements; conditionalElements.push( {
'element': element,
'predicate': predicate
} );
element.hidden = !predicate( value );
}; };
/** /**
* @param element - The element to do the operation on * @param element - The element to do the operation on
* @param onTrue - The classes (as strings) to set if true(ish) * @param onTrue - The classes (as strings) to set if true(ish)
@@ -107,7 +137,8 @@ export const ref = <T>( elements: HTMLElement[], data: T ): Ref<T> => {
return { return {
set, set,
get, get,
setConditionalElements, addAdditionalElement,
addConditionalElementBind,
addConditionalClasses, addConditionalClasses,
bind, bind,
onChange onChange

View File

@@ -1,7 +1,8 @@
export interface Ref<T> { export interface Ref<T> {
'set': ( data: T ) => void; 'set': ( data: T ) => void;
'get': () => T; '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; 'addConditionalClasses': ( element: HTMLElement, onTrue: string, onFalse: string ) => void;
'bind': ( element: HTMLInputElement, castFunction: ( val: string ) => T ) => void; 'bind': ( element: HTMLInputElement, castFunction: ( val: string ) => T ) => void;
'onChange': ( callback: () => void ) => void; 'onChange': ( callback: () => void ) => void;