history, reduced-motion, editable name box, an easter egg
This commit is contained in:
parent
d52151e787
commit
0a59aa66f6
14 changed files with 782 additions and 127 deletions
128
rainbow-quox/script/history.ts
Normal file
128
rainbow-quox/script/history.ts
Normal file
|
@ -0,0 +1,128 @@
|
|||
import { Colors as Oklchs, Rgbs } from './color.js';
|
||||
import * as Color from './color.js';
|
||||
|
||||
export class HistoryItem {
|
||||
name: string;
|
||||
oklch: Oklchs;
|
||||
rgb: Rgbs;
|
||||
|
||||
constructor(name: string, oklch: Oklchs, rgb: Rgbs) {
|
||||
this.oklch = oklch;
|
||||
this.rgb = rgb;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
asHtml(): HTMLButtonElement {
|
||||
const { lines: bg, outer, belly1: belly, fins1: fins } = this.rgb;
|
||||
|
||||
const content = `
|
||||
<svg class=history-colors width=30 height=25 viewBox="-10 -10 140 120">
|
||||
<rect x=-10 y=-10 width=140 height=120 fill="${bg.css()}" />
|
||||
<path fill="${fins.css()}" d="M 60,0 h -57.73 v 100 z">
|
||||
<title>fin colour: ${fins.css()}</title>
|
||||
</path>
|
||||
<path fill="${belly.css()}" d="M 70,0 h 40 l -57.73,100 h -40 z">
|
||||
<title>belly colour: ${belly.css()}</title>
|
||||
</path>
|
||||
<path fill="${outer.css()}" d="M 120,0 v 100 h -57.73 z">
|
||||
<title>outer body colour: ${outer.css()}</title>
|
||||
</path>
|
||||
<desc>
|
||||
sample of the palette for ${this.name}.
|
||||
fin colour: ${fins.css()}.
|
||||
belly colour: ${belly.css()}.
|
||||
outer body colour: ${outer.css()}.
|
||||
</desc>
|
||||
</svg>
|
||||
<span class=history-name>${this.name}</span>
|
||||
`;
|
||||
|
||||
let button = document.createElement('button');
|
||||
button.className = 'history-item';
|
||||
button.dataset.name = this.name;
|
||||
button.innerHTML = content;
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class History {
|
||||
items: string[];
|
||||
|
||||
constructor(items: string[] = []) { this.items = items; }
|
||||
|
||||
add(name: string): void { this.items.push(name); }
|
||||
|
||||
*iterNames(maxLength?: number | null): Iterable<string> {
|
||||
let seen = new Set<string>;
|
||||
let done = 0;
|
||||
if (maxLength === undefined) maxLength = 100;
|
||||
|
||||
for (let i = this.items.length - 1; i >= 0; i--) {
|
||||
if (maxLength !== null && done > maxLength) break;
|
||||
const name = this.items[i]!;
|
||||
if (!name || seen.has(name)) continue;
|
||||
seen.add(name); done++;
|
||||
yield name;
|
||||
}
|
||||
}
|
||||
|
||||
*iterItems(maxLength?: number | null): Iterable<HistoryItem> {
|
||||
for (const name of this.iterNames(maxLength)) {
|
||||
const oklch = Color.colors(new Color.Rand(name), Color.KNOWN[name]);
|
||||
const rgbs = Color.toRgbs(oklch);
|
||||
|
||||
yield new HistoryItem(name, oklch, rgbs);
|
||||
}
|
||||
}
|
||||
|
||||
static validate(x: unknown): History | undefined {
|
||||
if (!Array.isArray(x)) return;
|
||||
if (!x.every(i => typeof i === 'string')) return;
|
||||
return new History(x);
|
||||
}
|
||||
|
||||
toJSON() { return this.items; }
|
||||
|
||||
save(persist = true) {
|
||||
const storage = persist ? localStorage : sessionStorage;
|
||||
storage.setItem('history', JSON.stringify(this));
|
||||
}
|
||||
|
||||
// if the json was invalid, return it
|
||||
// if no history exists just start a new one
|
||||
static load(): History | string {
|
||||
const json =
|
||||
sessionStorage.getItem('history') ??
|
||||
localStorage.getItem('history');
|
||||
if (json != null) {
|
||||
let h = History.validate(JSON.parse(json));
|
||||
if (h) { h.prune(); return h; }
|
||||
else return json;
|
||||
} else {
|
||||
return new History;
|
||||
}
|
||||
}
|
||||
|
||||
// if the json is invalid, discard it
|
||||
static loadOrClear(): History {
|
||||
const h = History.load();
|
||||
return h instanceof History ? h : new History;
|
||||
}
|
||||
|
||||
addSave(name: string, persist = true): void {
|
||||
this.add(name);
|
||||
this.save(persist);
|
||||
}
|
||||
|
||||
prune(maxLength?: number | null) {
|
||||
let keep = [];
|
||||
for (let name of this.iterNames(maxLength)) keep.push(name);
|
||||
this.items = keep.reverse();
|
||||
}
|
||||
|
||||
pruneSave(maxLength?: number | null, persist = true) {
|
||||
this.prune(maxLength);
|
||||
this.save(persist);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue