@proto-labs-inc/rig-core 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -18,11 +18,15 @@ export declare class RigStore {
18
18
  private values;
19
19
  private listeners;
20
20
  constructor(manifest: Manifest);
21
+ updateManifest(manifest: Manifest): void;
21
22
  get(key: string): string | undefined;
22
23
  set(key: string, value: string): void;
23
24
  setMany(entries: Array<[string, string]>): void;
24
25
  setState(id: string, variants: Record<string, string>): void;
25
26
  subscribe: (l: () => void) => () => void;
27
+ /** Restore the exact URL, including removed selections, without writing a
28
+ * new history entry while Back or Forward is traversing an existing one. */
29
+ restoreUrl(): void;
26
30
  private syncUrl;
27
31
  }
28
32
  export declare function postToFrame(msg: RigToFrame): void;
package/dist/index.js CHANGED
@@ -6,8 +6,9 @@
6
6
  */
7
7
  import { PROTOCOL_VERSION, STATE_PARAM, variantParam, applyVariantSelection, isFrameToRig, } from "@proto-labs-inc/wire";
8
8
  import { loadFrameTools } from "./frame-tools-loader.js";
9
+ import { observePageHistory } from "./page-history.js";
9
10
  export { STATE_PARAM, variantParam } from "@proto-labs-inc/wire";
10
- const RIG_VERSION = "0.2.1";
11
+ const RIG_VERSION = "0.2.3";
11
12
  export const INTERNAL_PREVIEW_THEME_PARAM = "__protoTheme";
12
13
  /** Mark the page light or dark (data-proto-theme and the "dark" class) for
13
14
  * a prototype that styles both. The page's own CSS decides color-scheme:
@@ -60,9 +61,12 @@ export class RigStore {
60
61
  hydrated = true;
61
62
  }
62
63
  if (hydrated) {
63
- window.history.replaceState(null, "", `?${params.toString()}`);
64
+ window.history.replaceState(null, "", `?${params.toString()}${window.location.hash || ""}`);
64
65
  }
65
66
  }
67
+ updateManifest(manifest) {
68
+ this.manifest = manifest;
69
+ }
66
70
  get(key) {
67
71
  return this.values.get(key);
68
72
  }
@@ -115,6 +119,21 @@ export class RigStore {
115
119
  this.listeners.delete(l);
116
120
  };
117
121
  };
122
+ /** Restore the exact URL, including removed selections, without writing a
123
+ * new history entry while Back or Forward is traversing an existing one. */
124
+ restoreUrl() {
125
+ const next = new Map();
126
+ for (const [key, value] of new URLSearchParams(window.location.search)) {
127
+ if (key === STATE_PARAM || key.startsWith("v."))
128
+ next.set(key, value);
129
+ }
130
+ const changed = next.size !== this.values.size ||
131
+ [...next].some(([key, value]) => this.values.get(key) !== value);
132
+ if (!changed)
133
+ return;
134
+ this.values = next;
135
+ this.listeners.forEach((listener) => listener());
136
+ }
118
137
  syncUrl() {
119
138
  const params = new URLSearchParams(window.location.search);
120
139
  for (const key of [...params.keys()]) {
@@ -123,9 +142,8 @@ export class RigStore {
123
142
  }
124
143
  for (const [k, v] of this.values)
125
144
  params.set(k, v);
126
- const search = `?${params.toString()}`;
145
+ const search = `?${params.toString()}${window.location.hash || ""}`;
127
146
  window.history.replaceState(null, "", search);
128
- postToFrame({ type: "state-changed", search: searchForFrame() });
129
147
  }
130
148
  }
131
149
  export function postToFrame(msg) {
@@ -203,6 +221,11 @@ async function captureThumbnail() {
203
221
  * Call client-side only.
204
222
  */
205
223
  export function wireRig(store, manifest) {
224
+ store.updateManifest(manifest);
225
+ const disposeHistory = observePageHistory(() => {
226
+ store.restoreUrl();
227
+ postToFrame({ type: "state-changed", search: searchForFrame() });
228
+ });
206
229
  const disposeTools = loadFrameTools(manifest.frameOrigin);
207
230
  let controlledTheme = false;
208
231
  const media = window.matchMedia("(prefers-color-scheme: dark)");
@@ -246,6 +269,7 @@ export function wireRig(store, manifest) {
246
269
  });
247
270
  }
248
271
  return () => {
272
+ disposeHistory();
249
273
  media.removeEventListener("change", onSystemTheme);
250
274
  window.removeEventListener("message", onMessage);
251
275
  disposeTools();
@@ -0,0 +1,4 @@
1
+ /** Every URL change in a prototype is a step the browser bar can revisit.
2
+ * Keep state-only history writes intact: routers use those for bookkeeping.
3
+ * Install after boot-time URL hydration, which is not a navigation. */
4
+ export declare function observePageHistory(onNavigate: () => void): () => void;
@@ -0,0 +1,38 @@
1
+ /** Every URL change in a prototype is a step the browser bar can revisit.
2
+ * Keep state-only history writes intact: routers use those for bookkeeping.
3
+ * Install after boot-time URL hydration, which is not a navigation. */
4
+ export function observePageHistory(onNavigate) {
5
+ const history = window.history;
6
+ const push = history.pushState;
7
+ const replace = history.replaceState;
8
+ let currentUrl = window.location.href;
9
+ const changed = () => {
10
+ const url = window.location.href;
11
+ if (url === currentUrl)
12
+ return;
13
+ currentUrl = url;
14
+ onNavigate();
15
+ };
16
+ const pushState = function (data, unused, url) {
17
+ push.call(this, data, unused, url);
18
+ changed();
19
+ };
20
+ const replaceState = function (data, unused, url) {
21
+ const next = url == null ? window.location.href : new URL(url, window.location.href).href;
22
+ const write = next === window.location.href ? replace : push;
23
+ write.call(this, data, unused, url);
24
+ changed();
25
+ };
26
+ history.pushState = pushState;
27
+ history.replaceState = replaceState;
28
+ window.addEventListener("popstate", changed);
29
+ window.addEventListener("hashchange", changed);
30
+ return () => {
31
+ if (history.pushState === pushState)
32
+ history.pushState = push;
33
+ if (history.replaceState === replaceState)
34
+ history.replaceState = replace;
35
+ window.removeEventListener("popstate", changed);
36
+ window.removeEventListener("hashchange", changed);
37
+ };
38
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proto-labs-inc/rig-core",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Proto's rig, framework-free: URL-state sync, the postMessage bridge to the Frame, and comment mode.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@proto-labs-inc/wire": "^0.2.0"
31
+ "@proto-labs-inc/wire": "^0.2.2"
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "~6.0.2",