Last active
February 27, 2024 21:39
-
-
Save djmill0326/61d622510bcab336d9e8a51d189ba80f to your computer and use it in GitHub Desktop.
it was all csv this whole time?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Value = (data, transform, validation) => ({ | |
data, cache: null, listeners: new Set(), | |
resolve: transform ? | |
function () { | |
if (!this.cache) this.cache = transform(this.data, this.index); | |
return this.cache; | |
} | |
: function () { return this.data; }, | |
_update_and_notify: function (value) { | |
const old_value = this.data; | |
if(value === old_value) return; | |
this.cache = null; | |
this.data = value; | |
this.listeners.forEach(listener => listener(this)); | |
}, | |
update: validation ? | |
function (value) { return validation(value, this.index) ? this._update_and_notify(value) : null; } | |
: function (value) { return this._update_and_notify(value); }, | |
listen: function (on_update) { this.listeners.add(on_update); }, | |
unlisten: function(on_update) { this.listeners.delete(on_update); }, | |
bind_to_event: function(element, event_name, transform=ev=>ev.target.value) { | |
element.addEventListener(event_name, event => this.value.update(transform(event))); | |
}, | |
element: function(on_update = function (value) { this.innerHTML = value.resolve(); }) { | |
const column = document.createElement("td"); | |
column.className = "column"; | |
column.innerHTML = this.resolve(); | |
this.listen(on_update.bind(column)); | |
return column; | |
}, | |
create_input: function(type="text", postprocess=x=>x) { | |
const input = document.createElement("input"); | |
input.type = type; | |
input.className = "bound-input"; | |
this.bind_to_event(input, "input"); | |
postprocess(input); | |
return input; | |
} | |
}); | |
const ComputedValue = (transform, ...values) => { | |
// why is it this easy to implement? | |
const computed_value = Value(null, () => transform(...values.map(value => value.resolve()))); | |
values.forEach(value => value.listen(_ => { computed_value.update(performance.now()) })); | |
return computed_value; | |
} | |
const List = (values) => ({ | |
values, | |
resolve: function () { return this.values.map(x => x.resolve()); } | |
}); | |
const Column = (data, transform, validation, index=0) => { | |
const value = Value(data, transform, validation); | |
value.index = index; | |
return value; | |
}; | |
const Row = (columns) => { | |
const list = List(columns); | |
list.element = function() { | |
const row = document.createElement("tr"); | |
row.className = "row"; | |
list.values.forEach(col => row.appendChild(col.element())); | |
return row; | |
}; | |
return list; | |
}; | |
const Table = (rows, column_decoration=[]) => { | |
const list = List(rows); | |
list.rows = rows.map(row => { | |
const decorated_row = Row(row.values.map((col, i) => column_decoration[i] ? ComputedValue(column_decoration[i], col) : col)); | |
console.log(decorated_row); | |
return decorated_row; | |
}); | |
list.element = function() { | |
const table = document.createElement("table"); | |
table.className = "table"; | |
list.rows.forEach(row => table.appendChild(row.element())); | |
return table; | |
}; | |
return list; | |
}; | |
const ReadCsv = (input, input_transform, output_transform, validation, column_decoration) => | |
Table(input.split("\n").map(row => | |
Row(row.split(",").map((column, i) => | |
Column( | |
input_transform ? input_transform(column.trim(), i) : column.trim(), | |
output_transform, validation, i)))), column_decoration); | |
const prettify = (pre, postprocess) => { | |
if (pre) return postprocess ? x => `${pre.toLowerCase()}${postprocess(x)}` : x => pre.toLowerCase() + x; | |
else return postprocess ? x => postprocess(x) : x => x; | |
} | |
const repeat = (x, n) => { | |
let output = ""; | |
for (let i = 0; i < n; i++) output += x; | |
return output; | |
} | |
const fix_length = (length, pad="0") => x => repeat(pad, Math.max(length - x.length, 0)) + x; | |
// example | |
const make_it_make_cents = x => x.toFixed(2) + " ."; | |
const negate = (f, intl=false) => intl ? x => "(" + f(-x) + ")" : x => "-" + f(-x); | |
const positive_money = prettify("$", make_it_make_cents); | |
const negative_money = negate(positive_money); | |
const money = label => prettify(label, x => x < 0 ? negative_money(x) : positive_money(x)); | |
const bold = x => "<b>" + x + "</b>"; | |
const column_decoration = [bold("Example A"), bold("Another Example"), bold("Yet another column"), bold("column 4")].map(name => money(name + ": ")); | |
const table = ReadCsv("1,3,7,7\n1,0,1,5", parseFloat, null, null, column_decoration); | |
const computed = ComputedValue((a, b) => money("first two values: ")(a + b), table.values[0].values[0], table.values[1].values[0]); | |
let fun = 0; | |
const timing = 200; | |
const step = 4; | |
const small = timing / step; | |
const randomize = document.createElement("button"); | |
randomize.onclick = () => table.values.forEach((row, i) => setTimeout(() => row.values.forEach((col, j) => setTimeout(() => col.update(col.resolve() + Math.log(++fun) * (Math.random() * 2 - 1)), j * small)), i * timing)); | |
randomize.innerText = "randomize!"; | |
document.body.append(table.element(), computed.element(), randomize); | |
document.body.attributeStyleMap.set("font-family", "monospace"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment