From 27af62591f64cf363e816cd3642ba2aef06751c5 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Thu, 27 Feb 2025 19:25:14 -0500 Subject: [PATCH] Simplify permalinks --- docs/assets/scripts/tweak/permalink.js | 98 +++++++++++++++----------- docs/docs/palettes/edit/tweak.js | 21 +++--- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/docs/assets/scripts/tweak/permalink.js b/docs/assets/scripts/tweak/permalink.js index 27031dfd4..05fa837f1 100644 --- a/docs/assets/scripts/tweak/permalink.js +++ b/docs/assets/scripts/tweak/permalink.js @@ -13,56 +13,35 @@ export default class Permalink extends URLSearchParams { return Object.fromEntries(this.entries()); } - #mappings = new WeakMap(); - - mapObject(obj, mapping = {}) { - this.#mappings.set(obj, mapping); - } - - readFrom(obj) { - let mapping = this.#mappings.get(obj) ?? {}; - let { keyFrom = IDENTITY, valueFrom = IDENTITY } = mapping; - - for (let key in obj) { - let value = obj[key]; - let mappedValue = valueFrom(value); - let mappedKey = keyFrom(key); - this.set(mappedKey, mappedValue); - } - } - - writeTo(obj) { - let mapping = this.#mappings.get(obj) ?? {}; - let { keyTo = IDENTITY, valueTo = IDENTITY, canExtend = false } = mapping; - - for (let [key, value] of this) { - let mappedKey = keyTo(key); - let mappedValue = valueTo(value); - - if (canExtend || mappedKey in obj) { - obj[mappedKey] = mappedValue; - } - } - } - set(key, value, defaultValue) { - let oldValue = this.get(key); + if (equals(value, defaultValue) || equals(value, '')) { + value = null; + } - if (!value || value == defaultValue) { + value ??= null; // undefined -> null + + let oldValue = Array.isArray(value) ? this.getAll(key) : this.get(key); + let changed = !equals(value, oldValue); + + if (!changed) { + // Nothing to do here + return; + } + + if (Array.isArray(value)) { super.delete(key); - - if (oldValue) { - this.changed = true; + value.sort(); // to aid future comparisons + for (let v of value) { + super.append(key, v); } + } else if (value === null) { + super.delete(key); } else { super.set(key, value); - - if (String(value) !== String(oldValue)) { - this.changed = true; - } } this.sort(); + this.changed ||= changed; } /** @@ -79,3 +58,40 @@ export default class Permalink extends URLSearchParams { } } } + +function equals(value, oldValue) { + if (Array.isArray(value) || Array.isArray(oldValue)) { + value = toArray(value); + oldValue = toArray(oldValue); + + if (value.length !== oldValue.length) { + return false; + } + + return value.every((v, i) => equals(v, oldValue[i])); + } + + // (value ?? oldValue ?? true) returns true if they're both empty (null or undefined) + [value, oldValue] = [value, oldValue].map(v => (!v && v !== false && v !== 0 ? null : v)); + return value === oldValue || String(value) === String(oldValue); +} + +/** + * Convert a value to an array. `undefined` and `null` values are converted to an empty array. + * @param {*} value - The value to convert. + * @returns {any[]} The converted array. + */ +function toArray(value) { + value ??= []; + + if (Array.isArray(value)) { + return value; + } + + // Don't convert "foo" into ["f", "o", "o"] + if (typeof value !== 'string' && typeof value[Symbol.iterator] === 'function') { + return Array.from(value); + } + + return [value]; +} diff --git a/docs/docs/palettes/edit/tweak.js b/docs/docs/palettes/edit/tweak.js index 605b45ddb..d08c566e8 100644 --- a/docs/docs/palettes/edit/tweak.js +++ b/docs/docs/palettes/edit/tweak.js @@ -93,20 +93,16 @@ let paletteAppSpec = { // Non-reactive variables to expose Object.assign(this, { moreHue, HUE_RANGES, L_RANGES, hues, tints, MAX_CHROMA_BOUNDS }); - // Read URL params and apply them. This facilitates permalinks. - this.permalink.mapObject(this.hueShifts, { - keyTo: key => key.replace(/-shift$/, ''), - keyFrom: key => key + '-shift', - valueFrom: value => (!value ? '' : Number(value)), - valueTo: value => (!value ? 0 : Number(value)), - }); - this.grayChroma = this.originalGrayChroma; this.grayColor = this.originalGrayColor; if (location.search) { - // Update from URL - this.permalink.writeTo(this.hueShifts); + // Read URL params and apply them. This facilitates permalinks. + for (let hue in this.hueShifts) { + if (this.permalink.has(hue + '-shift')) { + this.hueShifts[hue] = Number(this.permalink.get(hue + '-shift')); + } + } for (let param of ['chroma-scale', 'gray-color', 'gray-chroma']) { if (this.permalink.has(param)) { @@ -203,6 +199,7 @@ let paletteAppSpec = { } if (!firstSeedHue) { + // No valid seed colors, abort mission return this.originalColors; } @@ -477,7 +474,9 @@ let paletteAppSpec = { hueShifts: { deep: true, handler() { - this.permalink.readFrom(this.hueShifts); + for (let hue in this.hueShifts) { + this.permalink.set(hue + '-shift', this.hueShifts[hue], 0); + } }, },