@proto-labs-inc/rig-core 0.2.0 → 0.2.2
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 +8 -0
- package/dist/index.js +32 -5
- package/dist/page-history.d.ts +4 -0
- package/dist/page-history.js +38 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
import { type Manifest, type ResolvedPreviewTheme, type RigToFrame } from "@proto-labs-inc/wire";
|
|
8
8
|
export { STATE_PARAM, variantParam } from "@proto-labs-inc/wire";
|
|
9
9
|
export declare const INTERNAL_PREVIEW_THEME_PARAM = "__protoTheme";
|
|
10
|
+
/** Mark the page light or dark (data-proto-theme and the "dark" class) for
|
|
11
|
+
* a prototype that styles both. The page's own CSS decides color-scheme:
|
|
12
|
+
* forcing it here gave pages that only style light mode dark form fields
|
|
13
|
+
* on a light page whenever the viewer's system was dark. */
|
|
10
14
|
export declare function applyPreviewTheme(theme: ResolvedPreviewTheme): void;
|
|
11
15
|
/** All addressable state, mirrored to the URL. Keys: "state" | "v.<component>". */
|
|
12
16
|
export declare class RigStore {
|
|
@@ -14,11 +18,15 @@ export declare class RigStore {
|
|
|
14
18
|
private values;
|
|
15
19
|
private listeners;
|
|
16
20
|
constructor(manifest: Manifest);
|
|
21
|
+
updateManifest(manifest: Manifest): void;
|
|
17
22
|
get(key: string): string | undefined;
|
|
18
23
|
set(key: string, value: string): void;
|
|
19
24
|
setMany(entries: Array<[string, string]>): void;
|
|
20
25
|
setState(id: string, variants: Record<string, string>): void;
|
|
21
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;
|
|
22
30
|
private syncUrl;
|
|
23
31
|
}
|
|
24
32
|
export declare function postToFrame(msg: RigToFrame): void;
|
package/dist/index.js
CHANGED
|
@@ -6,16 +6,20 @@
|
|
|
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.
|
|
11
|
+
const RIG_VERSION = "0.2.2";
|
|
11
12
|
export const INTERNAL_PREVIEW_THEME_PARAM = "__protoTheme";
|
|
13
|
+
/** Mark the page light or dark (data-proto-theme and the "dark" class) for
|
|
14
|
+
* a prototype that styles both. The page's own CSS decides color-scheme:
|
|
15
|
+
* forcing it here gave pages that only style light mode dark form fields
|
|
16
|
+
* on a light page whenever the viewer's system was dark. */
|
|
12
17
|
export function applyPreviewTheme(theme) {
|
|
13
18
|
if (typeof document === "undefined")
|
|
14
19
|
return;
|
|
15
20
|
const root = document.documentElement;
|
|
16
21
|
root.dataset.protoTheme = theme;
|
|
17
22
|
root.classList.toggle("dark", theme === "dark");
|
|
18
|
-
root.style.colorScheme = theme;
|
|
19
23
|
}
|
|
20
24
|
function resolvedSystemTheme() {
|
|
21
25
|
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
@@ -57,9 +61,12 @@ export class RigStore {
|
|
|
57
61
|
hydrated = true;
|
|
58
62
|
}
|
|
59
63
|
if (hydrated) {
|
|
60
|
-
window.history.replaceState(null, "", `?${params.toString()}`);
|
|
64
|
+
window.history.replaceState(null, "", `?${params.toString()}${window.location.hash || ""}`);
|
|
61
65
|
}
|
|
62
66
|
}
|
|
67
|
+
updateManifest(manifest) {
|
|
68
|
+
this.manifest = manifest;
|
|
69
|
+
}
|
|
63
70
|
get(key) {
|
|
64
71
|
return this.values.get(key);
|
|
65
72
|
}
|
|
@@ -112,6 +119,21 @@ export class RigStore {
|
|
|
112
119
|
this.listeners.delete(l);
|
|
113
120
|
};
|
|
114
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
|
+
}
|
|
115
137
|
syncUrl() {
|
|
116
138
|
const params = new URLSearchParams(window.location.search);
|
|
117
139
|
for (const key of [...params.keys()]) {
|
|
@@ -120,9 +142,8 @@ export class RigStore {
|
|
|
120
142
|
}
|
|
121
143
|
for (const [k, v] of this.values)
|
|
122
144
|
params.set(k, v);
|
|
123
|
-
const search = `?${params.toString()}`;
|
|
145
|
+
const search = `?${params.toString()}${window.location.hash || ""}`;
|
|
124
146
|
window.history.replaceState(null, "", search);
|
|
125
|
-
postToFrame({ type: "state-changed", search: searchForFrame() });
|
|
126
147
|
}
|
|
127
148
|
}
|
|
128
149
|
export function postToFrame(msg) {
|
|
@@ -200,6 +221,11 @@ async function captureThumbnail() {
|
|
|
200
221
|
* Call client-side only.
|
|
201
222
|
*/
|
|
202
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
|
+
});
|
|
203
229
|
const disposeTools = loadFrameTools(manifest.frameOrigin);
|
|
204
230
|
let controlledTheme = false;
|
|
205
231
|
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
@@ -243,6 +269,7 @@ export function wireRig(store, manifest) {
|
|
|
243
269
|
});
|
|
244
270
|
}
|
|
245
271
|
return () => {
|
|
272
|
+
disposeHistory();
|
|
246
273
|
media.removeEventListener("change", onSystemTheme);
|
|
247
274
|
window.removeEventListener("message", onMessage);
|
|
248
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.
|
|
3
|
+
"version": "0.2.2",
|
|
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.
|
|
31
|
+
"@proto-labs-inc/wire": "^0.2.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"typescript": "~6.0.2",
|