Skip to main content

max / goingson

3.3 KB · 99 lines History Blame Raw
1 /**
2 * GoingsOn — Query State Helper
3 *
4 * Phase 7 Tier 4 — every filter / sort / view-mode setting that changes what
5 * the user sees must be mirrored to `location.search` so reload, deep-link,
6 * and back-button preserve the state (see docs/design-system.md § Filter &
7 * view state in the URL). One helper, one set of conventions.
8 *
9 * Conventions:
10 * - Writes use `history.replaceState` so each filter change doesn't push a
11 * new history entry. The back button still works for view navigation
12 * (which uses router.js's pushState).
13 * - Empty values are removed from the URL — defaults render as a clean path.
14 * - On view-switch, `router.js#navigate(path)` writes only the new path,
15 * wiping the previous view's query. Each view restores its own state on
16 * load via `read(name)`.
17 */
18
19 (function() {
20 'use strict';
21
22 /**
23 * Read a single query-string value by name. Returns null if absent or empty.
24 */
25 function read(name) {
26 const v = new URLSearchParams(location.search).get(name);
27 return v == null || v === '' ? null : v;
28 }
29
30 /**
31 * Read multiple keys at once. Returns an object; absent keys are omitted.
32 */
33 function readMany(names) {
34 const params = new URLSearchParams(location.search);
35 const out = {};
36 for (const n of names) {
37 const v = params.get(n);
38 if (v != null && v !== '') out[n] = v;
39 }
40 return out;
41 }
42
43 /**
44 * Write a single value. Removes the key when value is null / undefined / empty / false.
45 * Uses replaceState — does not push history.
46 */
47 function write(name, value) {
48 const params = new URLSearchParams(location.search);
49 if (value == null || value === '' || value === false) {
50 params.delete(name);
51 } else if (value === true) {
52 params.set(name, '1');
53 } else {
54 params.set(name, String(value));
55 }
56 const qs = params.toString();
57 const path = location.pathname + (qs ? '?' + qs : '');
58 try {
59 history.replaceState({ path }, '', path);
60 } catch (_) { /* ignore — file:// or sandboxed contexts */ }
61 }
62
63 /**
64 * Batch-write. Same key removal rules as `write`.
65 */
66 function writeMany(obj) {
67 const params = new URLSearchParams(location.search);
68 for (const [name, value] of Object.entries(obj)) {
69 if (value == null || value === '' || value === false) {
70 params.delete(name);
71 } else if (value === true) {
72 params.set(name, '1');
73 } else {
74 params.set(name, String(value));
75 }
76 }
77 const qs = params.toString();
78 const path = location.pathname + (qs ? '?' + qs : '');
79 try {
80 history.replaceState({ path }, '', path);
81 } catch (_) { /* ignore */ }
82 }
83
84 /**
85 * Remove a list of keys.
86 */
87 function clear(names) {
88 const params = new URLSearchParams(location.search);
89 for (const n of names) params.delete(n);
90 const qs = params.toString();
91 const path = location.pathname + (qs ? '?' + qs : '');
92 try {
93 history.replaceState({ path }, '', path);
94 } catch (_) { /* ignore */ }
95 }
96
97 GoingsOn.queryState = { read, readMany, write, writeMany, clear };
98 })();
99