Skip to content

Instantly share code, notes, and snippets.

@djmill0326
Last active February 27, 2024 21:39
Show Gist options
  • Save djmill0326/61d622510bcab336d9e8a51d189ba80f to your computer and use it in GitHub Desktop.
Save djmill0326/61d622510bcab336d9e8a51d189ba80f to your computer and use it in GitHub Desktop.
it was all csv this whole time?
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