mirror of
https://github.com/janishutz/fundamentals-of-webengineering.git
synced 2025-11-25 05:44:24 +00:00
Rendering engine, simple refs almost complete, list refs start
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
import {
|
||||
CSV_Data
|
||||
} from './types';
|
||||
import {
|
||||
csv2json
|
||||
} from 'json-2-csv';
|
||||
|
||||
export type CSV_Data = Array<Record<string, unknown>>;
|
||||
|
||||
const convertCSVtoJSON = async ( csvText: string ) => {
|
||||
// Type cast OK, as the typing of the external library is not perfect.
|
||||
// NOTE: On transpilation to JS, it will be (more or less) disregarded anyway
|
||||
// Type cast OK, as the typing of the external library is not perfect -> Actually it is.
|
||||
// NOTE: On transpilation to JS, it will be (more or less) disregarded anyway.
|
||||
// If you claim it isn't good typing, it's the same as expecting it to guess the typing,
|
||||
// which is literally impossible in TS. But sure...
|
||||
return ( await csv2json( csvText ) ) as CSV_Data;
|
||||
};
|
||||
|
||||
|
||||
140
task_2_ts/ts/framework.ts
Normal file
140
task_2_ts/ts/framework.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
// Yes, I could not be arsed to keep track of state manually, so wrote a framework real quick that
|
||||
// does that for me. I am well aware that this is well over engineered, but it was a lot of fun
|
||||
// and no, this is *NOT* AI generated (I know Claude likes to hallucinate that kinda stuff)
|
||||
// I will be trying to somewhat follow Vue naming here, as that is what I am familiar with
|
||||
//
|
||||
// It was also a nice exercise to get familiar with Generics in TypeScript, something I haven't
|
||||
// really used before
|
||||
|
||||
export interface Ref<T> {
|
||||
'set': ( data: T ) => void;
|
||||
'get': () => T;
|
||||
'addConditionalElements': ( elements: HTMLElement[] ) => void;
|
||||
}
|
||||
|
||||
export interface ListRef<T> {
|
||||
'set': ( data: T[] ) => void;
|
||||
'get': () => T[];
|
||||
'sort': ( compare: ( a: T, b: T ) => number ) => void;
|
||||
'filter': ( predicate: ( value: T ) => boolean ) => void;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* @param data - The data stored in this ref
|
||||
* @param elements - The elements to bind to
|
||||
*/
|
||||
const ref = <T>( elements: HTMLElement[], data: T ): Ref<T> => {
|
||||
let value: T = data;
|
||||
let conditionalElements: HTMLElement[] = [];
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
const get = (): T => {
|
||||
return value;
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
const set = ( data: T ): void => {
|
||||
value = data;
|
||||
|
||||
// Update normal ref elements
|
||||
elements.forEach( el => {
|
||||
el.innerHTML = String( data );
|
||||
} );
|
||||
|
||||
// Update conditional elements
|
||||
conditionalElements.forEach( el => {
|
||||
// convert to boolean (explicitly)
|
||||
el.hidden = Boolean( data );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Add elements to be rendered conditionally on this ref. Treats type as booleanish
|
||||
* @param elements - The elements that are rendered consistently
|
||||
*/
|
||||
const addConditionalElements = ( elements: HTMLElement[] ): void => {
|
||||
conditionalElements = elements;
|
||||
};
|
||||
|
||||
return {
|
||||
set,
|
||||
get,
|
||||
addConditionalElements
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
// ╭───────────────────────────────────────────────╮
|
||||
// │ List ref, dynamic list rendering │
|
||||
// ╰───────────────────────────────────────────────╯
|
||||
export type HTMLTagNames = 'div' | 'button' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'span';
|
||||
|
||||
export interface RenderTemplate {
|
||||
[name: string]: {
|
||||
'type': HTMLTagNames;
|
||||
'attribute': string;
|
||||
'cssClasses': string[];
|
||||
}
|
||||
}
|
||||
|
||||
interface DiffList<T> {
|
||||
'modified': T[];
|
||||
'added': T[];
|
||||
'removed': T[];
|
||||
}
|
||||
|
||||
const listRef = <T>( parent: HTMLElement, data: T[] ): ListRef<T> => {
|
||||
let value: T[] = data;
|
||||
let rendered: T[] = [];
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
const get = (): T[] => {
|
||||
return value;
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
const set = ( data: T[] ): void => {
|
||||
const diffList: DiffList<T> = {
|
||||
'modified': [],
|
||||
'added': [],
|
||||
'removed': []
|
||||
};
|
||||
|
||||
value = data;
|
||||
rendered = value;
|
||||
};
|
||||
|
||||
const sort = ( compare: ( a: T, b: T ) => number ): void => {
|
||||
// Re-render based on compare function
|
||||
};
|
||||
|
||||
const filter = ( predicate: ( value: T ) => boolean ): void => {
|
||||
const diffList: DiffList<T> = {
|
||||
'modified': [],
|
||||
'added': [],
|
||||
'removed': []
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
sort,
|
||||
filter
|
||||
};
|
||||
};
|
||||
|
||||
// The list renderer using a DiffList
|
||||
const renderList = <T>( diffList: DiffList<T> ) => {
|
||||
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
ref,
|
||||
listRef
|
||||
};
|
||||
1
task_2_ts/ts/types.d.ts
vendored
Normal file
1
task_2_ts/ts/types.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export type CSV_Data = Array<Record<string, unknown>>;
|
||||
Reference in New Issue
Block a user