@vkma.dev/router 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/dist/core/bridge-like.d.ts +25 -0
- package/dist/core/bridge-like.d.ts.map +1 -0
- package/dist/core/bridge-like.js +13 -0
- package/dist/core/bridge-like.js.map +1 -0
- package/dist/core/bridge-sync.d.ts +37 -0
- package/dist/core/bridge-sync.d.ts.map +1 -0
- package/dist/core/bridge-sync.js +58 -0
- package/dist/core/bridge-sync.js.map +1 -0
- package/dist/core/hash-params.d.ts +40 -0
- package/dist/core/hash-params.d.ts.map +1 -0
- package/dist/core/hash-params.js +60 -0
- package/dist/core/hash-params.js.map +1 -0
- package/dist/core/loose-route.js +9 -0
- package/dist/core/loose-route.js.map +1 -0
- package/dist/core/panels-stack.d.ts +28 -0
- package/dist/core/panels-stack.d.ts.map +1 -0
- package/dist/core/panels-stack.js +60 -0
- package/dist/core/panels-stack.js.map +1 -0
- package/dist/core/state-keys.d.ts +10 -0
- package/dist/core/state-keys.d.ts.map +1 -0
- package/dist/core/state-keys.js +21 -0
- package/dist/core/state-keys.js.map +1 -0
- package/dist/core/swipe-back.d.ts +25 -0
- package/dist/core/swipe-back.d.ts.map +1 -0
- package/dist/core/swipe-back.js +53 -0
- package/dist/core/swipe-back.js.map +1 -0
- package/dist/core/vk-history.d.ts +41 -0
- package/dist/core/vk-history.d.ts.map +1 -0
- package/dist/core/vk-history.js +115 -0
- package/dist/core/vk-history.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/preset/app.d.ts +35 -0
- package/dist/preset/app.d.ts.map +1 -0
- package/dist/preset/app.js +134 -0
- package/dist/preset/app.js.map +1 -0
- package/dist/preset/builders.d.ts +209 -0
- package/dist/preset/builders.d.ts.map +1 -0
- package/dist/preset/builders.js +209 -0
- package/dist/preset/builders.js.map +1 -0
- package/dist/preset/define-panel.d.ts +77 -0
- package/dist/preset/define-panel.d.ts.map +1 -0
- package/dist/preset/define-panel.js +42 -0
- package/dist/preset/define-panel.js.map +1 -0
- package/dist/preset/define-view.d.ts +42 -0
- package/dist/preset/define-view.d.ts.map +1 -0
- package/dist/preset/define-view.js +43 -0
- package/dist/preset/define-view.js.map +1 -0
- package/dist/preset/inflate.d.ts +17 -0
- package/dist/preset/inflate.d.ts.map +1 -0
- package/dist/preset/inflate.js +40 -0
- package/dist/preset/inflate.js.map +1 -0
- package/dist/preset/navigator.d.ts +75 -0
- package/dist/preset/navigator.d.ts.map +1 -0
- package/dist/preset/navigator.js +116 -0
- package/dist/preset/navigator.js.map +1 -0
- package/dist/preset.d.ts +17 -0
- package/dist/preset.js +16 -0
- package/dist/react/hooks.d.ts +69 -0
- package/dist/react/hooks.d.ts.map +1 -0
- package/dist/react/hooks.js +101 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +1 -0
- package/dist/vkui/choreography.js +59 -0
- package/dist/vkui/choreography.js.map +1 -0
- package/dist/vkui/modals.d.ts +49 -0
- package/dist/vkui/modals.d.ts.map +1 -0
- package/dist/vkui/modals.js +116 -0
- package/dist/vkui/modals.js.map +1 -0
- package/dist/vkui/not-found.d.ts +9 -0
- package/dist/vkui/not-found.d.ts.map +1 -0
- package/dist/vkui/not-found.js +23 -0
- package/dist/vkui/not-found.js.map +1 -0
- package/dist/vkui/popouts.d.ts +34 -0
- package/dist/vkui/popouts.d.ts.map +1 -0
- package/dist/vkui/popouts.js +69 -0
- package/dist/vkui/popouts.js.map +1 -0
- package/dist/vkui/vk-view.d.ts +26 -0
- package/dist/vkui/vk-view.d.ts.map +1 -0
- package/dist/vkui/vk-view.js +81 -0
- package/dist/vkui/vk-view.js.map +1 -0
- package/dist/vkui.d.ts +5 -0
- package/dist/vkui.js +5 -0
- package/package.json +70 -3
- package/src/core/bridge-like.ts +25 -0
- package/src/core/bridge-sync.ts +95 -0
- package/src/core/hash-params.ts +73 -0
- package/src/core/loose-route.ts +17 -0
- package/src/core/panels-stack.ts +85 -0
- package/src/core/state-keys.ts +39 -0
- package/src/core/swipe-back.ts +80 -0
- package/src/core/vk-history.ts +146 -0
- package/src/index.ts +30 -0
- package/src/preset/app.tsx +192 -0
- package/src/preset/builders.tsx +566 -0
- package/src/preset/define-panel.ts +109 -0
- package/src/preset/define-view.ts +73 -0
- package/src/preset/inflate.ts +57 -0
- package/src/preset/navigator.ts +250 -0
- package/src/preset.ts +51 -0
- package/src/react/hooks.ts +168 -0
- package/src/react.ts +11 -0
- package/src/vkui/choreography.ts +105 -0
- package/src/vkui/modals.tsx +216 -0
- package/src/vkui/not-found.tsx +21 -0
- package/src/vkui/popouts.tsx +106 -0
- package/src/vkui/vk-view.tsx +147 -0
- package/src/vkui.ts +17 -0
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pajecawav
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/core/bridge-like.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The structural bridge contract: type-compatible with both
|
|
4
|
+
* `@vkontakte/vk-bridge` (whose subscribe handlers receive
|
|
5
|
+
* `{ detail: { type, data } }` CustomEvents) and `@vkma.dev/bridge`
|
|
6
|
+
* (flat `{ type, data }`). The router never hard-depends on either.
|
|
7
|
+
*/
|
|
8
|
+
export interface BridgeEventEnvelope {
|
|
9
|
+
type?: string;
|
|
10
|
+
data?: unknown;
|
|
11
|
+
detail?: {
|
|
12
|
+
type?: string;
|
|
13
|
+
data?: unknown;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface VkBridgeLike {
|
|
17
|
+
send(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
18
|
+
subscribe(handler: (event: BridgeEventEnvelope) => void): () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function normalizeBridgeEvent(event: BridgeEventEnvelope): {
|
|
21
|
+
type: string;
|
|
22
|
+
data: unknown;
|
|
23
|
+
} | null;
|
|
24
|
+
//#endregion
|
|
25
|
+
//# sourceMappingURL=bridge-like.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-like.d.ts","names":[],"sources":["../../src/core/bridge-like.ts"],"mappings":";;;;;;;iBAOiB;EAChB;EACA;EACA;IAAW;IAAe;;;iBAGV;EAChB,KAAK,gBAAgB,SAAS,0BAA0B;EACxD,UAAU,UAAU,OAAO;;wBAGZ,qBACf,OAAO;EACH;EAAc"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/core/bridge-like.ts
|
|
2
|
+
function normalizeBridgeEvent(event) {
|
|
3
|
+
const type = event.detail?.type ?? event.type;
|
|
4
|
+
const data = event.detail !== void 0 ? event.detail.data : event.data;
|
|
5
|
+
return type !== void 0 ? {
|
|
6
|
+
type,
|
|
7
|
+
data
|
|
8
|
+
} : null;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { normalizeBridgeEvent };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=bridge-like.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-like.js","names":[],"sources":["../../src/core/bridge-like.ts"],"sourcesContent":["/**\n * The structural bridge contract: type-compatible with both\n * `@vkontakte/vk-bridge` (whose subscribe handlers receive\n * `{ detail: { type, data } }` CustomEvents) and `@vkma.dev/bridge`\n * (flat `{ type, data }`). The router never hard-depends on either.\n */\n\nexport interface BridgeEventEnvelope {\n\ttype?: string;\n\tdata?: unknown;\n\tdetail?: { type?: string; data?: unknown };\n}\n\nexport interface VkBridgeLike {\n\tsend(method: string, params?: Record<string, unknown>): Promise<unknown>;\n\tsubscribe(handler: (event: BridgeEventEnvelope) => void): () => void;\n}\n\nexport function normalizeBridgeEvent(\n\tevent: BridgeEventEnvelope,\n): { type: string; data: unknown } | null {\n\tconst type = event.detail?.type ?? event.type;\n\tconst data = event.detail !== undefined ? event.detail.data : event.data;\n\treturn type !== undefined ? { type, data } : null;\n}\n"],"mappings":";AAkBA,SAAgB,qBACf,OACyC;CACzC,MAAM,OAAO,MAAM,QAAQ,QAAQ,MAAM;CACzC,MAAM,OAAO,MAAM,WAAW,KAAA,IAAY,MAAM,OAAO,OAAO,MAAM;CACpE,OAAO,SAAS,KAAA,IAAY;EAAE;EAAM;CAAK,IAAI;AAC9C"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { VkBridgeLike } from "./bridge-like.js";
|
|
2
|
+
//#region src/core/bridge-sync.d.ts
|
|
3
|
+
export interface BridgeSyncOptions {
|
|
4
|
+
history: {
|
|
5
|
+
location: {
|
|
6
|
+
pathname: string;
|
|
7
|
+
search: string;
|
|
8
|
+
hash: string;
|
|
9
|
+
};
|
|
10
|
+
subscribe(listener: (event: {
|
|
11
|
+
location: {
|
|
12
|
+
pathname: string;
|
|
13
|
+
search: string;
|
|
14
|
+
hash: string;
|
|
15
|
+
};
|
|
16
|
+
}) => void): () => void;
|
|
17
|
+
replace(path: string): void;
|
|
18
|
+
};
|
|
19
|
+
bridge: VkBridgeLike;
|
|
20
|
+
/**
|
|
21
|
+
* SetLocation payload for a committed location. Defaults to the hash
|
|
22
|
+
* mode (the hash content); hash-param users pass the codecs from
|
|
23
|
+
* `createVkHistory`.
|
|
24
|
+
*/
|
|
25
|
+
serializePayload?: (location: {
|
|
26
|
+
pathname: string;
|
|
27
|
+
search: string;
|
|
28
|
+
hash: string;
|
|
29
|
+
}) => string;
|
|
30
|
+
/** Router href from a SetLocation/ChangeFragment payload. */
|
|
31
|
+
parsePayload?: (payload: string) => string;
|
|
32
|
+
}
|
|
33
|
+
export declare function createBridgeSync(options: BridgeSyncOptions): {
|
|
34
|
+
destroy(): void;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//# sourceMappingURL=bridge-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-sync.d.ts","names":[],"sources":["../../src/core/bridge-sync.ts"],"mappings":";;iBAqBiB;EAChB;IACC;MAAY;MAAkB;MAAgB;;IAC9C,UACC,WAAW;MACV;QAAY;QAAkB;QAAgB;;;IAGhD,QAAQ;;EAET,QAAQ;;;;;;EAMR,oBAAoB;IAAY;IAAkB;IAAgB;;;EAElE,gBAAgB;;wBASD,iBAAiB,SAAS;EAAsB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { parseHashLocation, serializeHashLocation } from "./hash-params.js";
|
|
2
|
+
import { normalizeBridgeEvent } from "./bridge-like.js";
|
|
3
|
+
//#region src/core/bridge-sync.ts
|
|
4
|
+
/**
|
|
5
|
+
* The host↔app location sync contract:
|
|
6
|
+
* - every committed history change → `VKWebAppSetLocation`
|
|
7
|
+
* (`replace_state: true`) so the host URL/share reflects the app;
|
|
8
|
+
* - incoming `VKWebAppChangeFragment` / `VKWebAppLocationChanged`
|
|
9
|
+
* (host-initiated jumps) → `history.replace(href)`;
|
|
10
|
+
* - loop protection: an incoming href equal to the current one is ignored.
|
|
11
|
+
*
|
|
12
|
+
* Payload codecs come from the history (they are mode-specific: hash mode
|
|
13
|
+
* sends the hash content, hash-param mode sends `?path=…` preserving
|
|
14
|
+
* foreign params). The initial location is sent once at creation.
|
|
15
|
+
* Serialization happens from the history EVENT's location: subscribers are
|
|
16
|
+
* notified synchronously before the microtask flush writes the URL, so
|
|
17
|
+
* `window.location.hash` is stale by one entry at notify time.
|
|
18
|
+
*/
|
|
19
|
+
const LOCATION_EVENTS = /* @__PURE__ */ new Set(["VKWebAppChangeFragment", "VKWebAppLocationChanged"]);
|
|
20
|
+
/** Hash-mode codecs are the defaults. */
|
|
21
|
+
const serializeDefault = (location) => serializeHashLocation(location);
|
|
22
|
+
const parseDefault = (payload) => parseHashLocation(payload);
|
|
23
|
+
function createBridgeSync(options) {
|
|
24
|
+
const serializePayload = options.serializePayload ?? serializeDefault;
|
|
25
|
+
const parsePayload = options.parsePayload ?? parseDefault;
|
|
26
|
+
let lastSent = "";
|
|
27
|
+
const sendLocation = (location) => {
|
|
28
|
+
const payload = serializePayload(location);
|
|
29
|
+
if (payload !== lastSent) {
|
|
30
|
+
lastSent = payload;
|
|
31
|
+
options.bridge.send("VKWebAppSetLocation", {
|
|
32
|
+
location: payload,
|
|
33
|
+
replace_state: true
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
sendLocation(options.history.location);
|
|
38
|
+
const unsubscribeHistory = options.history.subscribe((event) => {
|
|
39
|
+
sendLocation(event.location);
|
|
40
|
+
});
|
|
41
|
+
const unsubscribeBridge = options.bridge.subscribe((rawEvent) => {
|
|
42
|
+
const event = normalizeBridgeEvent(rawEvent);
|
|
43
|
+
if (event === null || !LOCATION_EVENTS.has(event.type)) return;
|
|
44
|
+
const data = event.data;
|
|
45
|
+
if (!data?.location) return;
|
|
46
|
+
const incoming = parsePayload(data.location);
|
|
47
|
+
if (incoming === `${options.history.location.pathname}${options.history.location.search}`) return;
|
|
48
|
+
options.history.replace(incoming);
|
|
49
|
+
});
|
|
50
|
+
return { destroy() {
|
|
51
|
+
unsubscribeHistory();
|
|
52
|
+
unsubscribeBridge();
|
|
53
|
+
} };
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { createBridgeSync };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=bridge-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-sync.js","names":[],"sources":["../../src/core/bridge-sync.ts"],"sourcesContent":["import { normalizeBridgeEvent, type VkBridgeLike } from \"./bridge-like.ts\";\nimport { parseHashLocation, serializeHashLocation } from \"./hash-params.ts\";\n\n/**\n * The host↔app location sync contract:\n * - every committed history change → `VKWebAppSetLocation`\n * (`replace_state: true`) so the host URL/share reflects the app;\n * - incoming `VKWebAppChangeFragment` / `VKWebAppLocationChanged`\n * (host-initiated jumps) → `history.replace(href)`;\n * - loop protection: an incoming href equal to the current one is ignored.\n *\n * Payload codecs come from the history (they are mode-specific: hash mode\n * sends the hash content, hash-param mode sends `?path=…` preserving\n * foreign params). The initial location is sent once at creation.\n * Serialization happens from the history EVENT's location: subscribers are\n * notified synchronously before the microtask flush writes the URL, so\n * `window.location.hash` is stale by one entry at notify time.\n */\n\nconst LOCATION_EVENTS = new Set([\"VKWebAppChangeFragment\", \"VKWebAppLocationChanged\"]);\n\nexport interface BridgeSyncOptions {\n\thistory: {\n\t\tlocation: { pathname: string; search: string; hash: string };\n\t\tsubscribe(\n\t\t\tlistener: (event: {\n\t\t\t\tlocation: { pathname: string; search: string; hash: string };\n\t\t\t}) => void,\n\t\t): () => void;\n\t\treplace(path: string): void;\n\t};\n\tbridge: VkBridgeLike;\n\t/**\n\t * SetLocation payload for a committed location. Defaults to the hash\n\t * mode (the hash content); hash-param users pass the codecs from\n\t * `createVkHistory`.\n\t */\n\tserializePayload?: (location: { pathname: string; search: string; hash: string }) => string;\n\t/** Router href from a SetLocation/ChangeFragment payload. */\n\tparsePayload?: (payload: string) => string;\n}\n\n/** Hash-mode codecs are the defaults. */\nconst serializeDefault = (location: { pathname: string; search: string; hash: string }): string =>\n\tserializeHashLocation(location);\n\nconst parseDefault = (payload: string): string => parseHashLocation(payload);\n\nexport function createBridgeSync(options: BridgeSyncOptions): { destroy(): void } {\n\tconst serializePayload = options.serializePayload ?? serializeDefault;\n\tconst parsePayload = options.parsePayload ?? parseDefault;\n\tlet lastSent = \"\";\n\n\tconst sendLocation = (location: { pathname: string; search: string; hash: string }): void => {\n\t\tconst payload = serializePayload(location);\n\t\tif (payload !== lastSent) {\n\t\t\tlastSent = payload;\n\t\t\tvoid options.bridge.send(\"VKWebAppSetLocation\", {\n\t\t\t\tlocation: payload,\n\t\t\t\treplace_state: true,\n\t\t\t});\n\t\t}\n\t};\n\n\t// the host must learn the location the app started at\n\tsendLocation(options.history.location);\n\n\tconst unsubscribeHistory = options.history.subscribe(event => {\n\t\tsendLocation(event.location);\n\t});\n\n\tconst unsubscribeBridge = options.bridge.subscribe(rawEvent => {\n\t\tconst event = normalizeBridgeEvent(rawEvent);\n\t\tif (event === null || !LOCATION_EVENTS.has(event.type)) {\n\t\t\treturn;\n\t\t}\n\t\tconst data = event.data as { location?: string } | undefined;\n\t\tif (!data?.location) {\n\t\t\treturn; // malformed — the host always sends a location; never act on it\n\t\t}\n\t\tconst incoming = parsePayload(data.location);\n\t\tconst current = `${options.history.location.pathname}${options.history.location.search}`;\n\t\tif (incoming === current) {\n\t\t\treturn; // echo of our own location — loop protection\n\t\t}\n\t\toptions.history.replace(incoming);\n\t});\n\n\treturn {\n\t\tdestroy() {\n\t\t\tunsubscribeHistory();\n\t\t\tunsubscribeBridge();\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,MAAM,kCAAkB,IAAI,IAAI,CAAC,0BAA0B,yBAAyB,CAAC;;AAwBrF,MAAM,oBAAoB,aACzB,sBAAsB,QAAQ;AAE/B,MAAM,gBAAgB,YAA4B,kBAAkB,OAAO;AAE3E,SAAgB,iBAAiB,SAAiD;CACjF,MAAM,mBAAmB,QAAQ,oBAAoB;CACrD,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,IAAI,WAAW;CAEf,MAAM,gBAAgB,aAAuE;EAC5F,MAAM,UAAU,iBAAiB,QAAQ;EACzC,IAAI,YAAY,UAAU;GACzB,WAAW;GACX,QAAa,OAAO,KAAK,uBAAuB;IAC/C,UAAU;IACV,eAAe;GAChB,CAAC;EACF;CACD;CAGA,aAAa,QAAQ,QAAQ,QAAQ;CAErC,MAAM,qBAAqB,QAAQ,QAAQ,WAAU,UAAS;EAC7D,aAAa,MAAM,QAAQ;CAC5B,CAAC;CAED,MAAM,oBAAoB,QAAQ,OAAO,WAAU,aAAY;EAC9D,MAAM,QAAQ,qBAAqB,QAAQ;EAC3C,IAAI,UAAU,QAAQ,CAAC,gBAAgB,IAAI,MAAM,IAAI,GACpD;EAED,MAAM,OAAO,MAAM;EACnB,IAAI,CAAC,MAAM,UACV;EAED,MAAM,WAAW,aAAa,KAAK,QAAQ;EAE3C,IAAI,aAAa,GADE,QAAQ,QAAQ,SAAS,WAAW,QAAQ,QAAQ,SAAS,UAE/E;EAED,QAAQ,QAAQ,QAAQ,QAAQ;CACjC,CAAC;CAED,OAAO,EACN,UAAU;EACT,mBAAmB;EACnB,kBAAkB;CACnB,EACD;AACD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/core/hash-params.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Hash payload helpers for the VK history.
|
|
4
|
+
*
|
|
5
|
+
* Two URL modes:
|
|
6
|
+
* - `hash` (default): the hash IS the location (`#/item/7?tab=x`),
|
|
7
|
+
* TanStack-style — parity with the old `createHashRouter`.
|
|
8
|
+
* - `hash-param`: the whole router location rides in ONE hash query param
|
|
9
|
+
* (`#?path=%2Fitem%2F7`), foreign hash params (launch params, UTM) are
|
|
10
|
+
* preserved next to it and never leak into the router's search. The real
|
|
11
|
+
* `location.search` — where VK puts launch params on the web platform —
|
|
12
|
+
* is never touched. Parity with the old `createHashParamRouter`.
|
|
13
|
+
*
|
|
14
|
+
* Both read paths tolerate LEGACY deep links written by the other mode
|
|
15
|
+
* (`#/path` resolves in hash-param mode, `#?path=…` resolves in hash
|
|
16
|
+
* mode); writes always use the active mode's canonical format.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readHashParams(win: Window): URLSearchParams;
|
|
19
|
+
/**
|
|
20
|
+
* Router href from the document hash, tolerant to both modes:
|
|
21
|
+
* `#?path=%2Flist&x=1` → `/list` (hash-param), `#/list?x=1` → `/list?x=1`
|
|
22
|
+
* (hash), empty or foreign hash → `/`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readHrefFromHash(win: Window, pathParam: string): string;
|
|
25
|
+
/** Serialize a router href into the hash, preserving foreign params. */
|
|
26
|
+
export declare function writeHashParams(win: Window, pathParam: string, href: string): string;
|
|
27
|
+
/** Hash content (`?…` string) for a router href — the SetLocation payload. */
|
|
28
|
+
export declare function serializeHashPayload(win: Window, pathParam: string, href: string): string;
|
|
29
|
+
/** Extract the router href from a SetLocation/ChangeFragment payload. */
|
|
30
|
+
export declare function parseHashPayload(payload: string, pathParam: string): string;
|
|
31
|
+
/** Hash-mode SetLocation payload: the hash content IS the location. */
|
|
32
|
+
export declare function serializeHashLocation(location: {
|
|
33
|
+
pathname: string;
|
|
34
|
+
search: string;
|
|
35
|
+
hash: string;
|
|
36
|
+
}): string;
|
|
37
|
+
/** Hash-mode payload → router href (normalized to a leading slash). */
|
|
38
|
+
export declare function parseHashLocation(payload: string): string;
|
|
39
|
+
//#endregion
|
|
40
|
+
//# sourceMappingURL=hash-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-params.d.ts","names":[],"sources":["../../src/core/hash-params.ts"],"mappings":";;;;;;;;;;;;;;;;;wBAiBgB,eAAe,KAAK,SAAS;;;;;;wBAS7B,iBAAiB,KAAK,QAAQ;;wBAY9B,gBAAgB,KAAK,QAAQ,mBAAmB;;wBAOhD,qBAAqB,KAAK,QAAQ,mBAAmB;;wBAOrD,iBAAiB,iBAAiB;;wBASlC,sBAAsB;EACrC;EACA;EACA;;;wBAMe,kBAAkB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/core/hash-params.ts
|
|
2
|
+
/**
|
|
3
|
+
* Hash payload helpers for the VK history.
|
|
4
|
+
*
|
|
5
|
+
* Two URL modes:
|
|
6
|
+
* - `hash` (default): the hash IS the location (`#/item/7?tab=x`),
|
|
7
|
+
* TanStack-style — parity with the old `createHashRouter`.
|
|
8
|
+
* - `hash-param`: the whole router location rides in ONE hash query param
|
|
9
|
+
* (`#?path=%2Fitem%2F7`), foreign hash params (launch params, UTM) are
|
|
10
|
+
* preserved next to it and never leak into the router's search. The real
|
|
11
|
+
* `location.search` — where VK puts launch params on the web platform —
|
|
12
|
+
* is never touched. Parity with the old `createHashParamRouter`.
|
|
13
|
+
*
|
|
14
|
+
* Both read paths tolerate LEGACY deep links written by the other mode
|
|
15
|
+
* (`#/path` resolves in hash-param mode, `#?path=…` resolves in hash
|
|
16
|
+
* mode); writes always use the active mode's canonical format.
|
|
17
|
+
*/
|
|
18
|
+
function readHashParams(win) {
|
|
19
|
+
return new URLSearchParams(win.location.hash.replace(/^#\?/, ""));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Router href from the document hash, tolerant to both modes:
|
|
23
|
+
* `#?path=%2Flist&x=1` → `/list` (hash-param), `#/list?x=1` → `/list?x=1`
|
|
24
|
+
* (hash), empty or foreign hash → `/`.
|
|
25
|
+
*/
|
|
26
|
+
function readHrefFromHash(win, pathParam) {
|
|
27
|
+
const hash = win.location.hash;
|
|
28
|
+
if (hash.startsWith("#?")) return new URLSearchParams(hash.slice(2)).get(pathParam) ?? "/";
|
|
29
|
+
if (hash.startsWith("#/")) return hash.slice(1);
|
|
30
|
+
return "/";
|
|
31
|
+
}
|
|
32
|
+
/** Serialize a router href into the hash, preserving foreign params. */
|
|
33
|
+
function writeHashParams(win, pathParam, href) {
|
|
34
|
+
const params = readHashParams(win);
|
|
35
|
+
params.set(pathParam, href);
|
|
36
|
+
return `${win.location.pathname}${win.location.search}#?${params.toString()}`;
|
|
37
|
+
}
|
|
38
|
+
/** Hash content (`?…` string) for a router href — the SetLocation payload. */
|
|
39
|
+
function serializeHashPayload(win, pathParam, href) {
|
|
40
|
+
const params = readHashParams(win);
|
|
41
|
+
params.set(pathParam, href);
|
|
42
|
+
return `?${params.toString()}`;
|
|
43
|
+
}
|
|
44
|
+
/** Extract the router href from a SetLocation/ChangeFragment payload. */
|
|
45
|
+
function parseHashPayload(payload, pathParam) {
|
|
46
|
+
if (payload.startsWith("/")) return payload;
|
|
47
|
+
return new URLSearchParams(payload.replace(/^\?/, "")).get(pathParam) ?? "/";
|
|
48
|
+
}
|
|
49
|
+
/** Hash-mode SetLocation payload: the hash content IS the location. */
|
|
50
|
+
function serializeHashLocation(location) {
|
|
51
|
+
return `${location.pathname}${location.search}${location.hash}`;
|
|
52
|
+
}
|
|
53
|
+
/** Hash-mode payload → router href (normalized to a leading slash). */
|
|
54
|
+
function parseHashLocation(payload) {
|
|
55
|
+
return payload.startsWith("/") ? payload : `/${payload}`;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { parseHashLocation, parseHashPayload, readHashParams, readHrefFromHash, serializeHashLocation, serializeHashPayload, writeHashParams };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=hash-params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-params.js","names":[],"sources":["../../src/core/hash-params.ts"],"sourcesContent":["/**\n * Hash payload helpers for the VK history.\n *\n * Two URL modes:\n * - `hash` (default): the hash IS the location (`#/item/7?tab=x`),\n * TanStack-style — parity with the old `createHashRouter`.\n * - `hash-param`: the whole router location rides in ONE hash query param\n * (`#?path=%2Fitem%2F7`), foreign hash params (launch params, UTM) are\n * preserved next to it and never leak into the router's search. The real\n * `location.search` — where VK puts launch params on the web platform —\n * is never touched. Parity with the old `createHashParamRouter`.\n *\n * Both read paths tolerate LEGACY deep links written by the other mode\n * (`#/path` resolves in hash-param mode, `#?path=…` resolves in hash\n * mode); writes always use the active mode's canonical format.\n */\n\nexport function readHashParams(win: Window): URLSearchParams {\n\treturn new URLSearchParams(win.location.hash.replace(/^#\\?/, \"\"));\n}\n\n/**\n * Router href from the document hash, tolerant to both modes:\n * `#?path=%2Flist&x=1` → `/list` (hash-param), `#/list?x=1` → `/list?x=1`\n * (hash), empty or foreign hash → `/`.\n */\nexport function readHrefFromHash(win: Window, pathParam: string): string {\n\tconst hash = win.location.hash;\n\tif (hash.startsWith(\"#?\")) {\n\t\treturn new URLSearchParams(hash.slice(2)).get(pathParam) ?? \"/\";\n\t}\n\tif (hash.startsWith(\"#/\")) {\n\t\treturn hash.slice(1);\n\t}\n\treturn \"/\";\n}\n\n/** Serialize a router href into the hash, preserving foreign params. */\nexport function writeHashParams(win: Window, pathParam: string, href: string): string {\n\tconst params = readHashParams(win);\n\tparams.set(pathParam, href);\n\treturn `${win.location.pathname}${win.location.search}#?${params.toString()}`;\n}\n\n/** Hash content (`?…` string) for a router href — the SetLocation payload. */\nexport function serializeHashPayload(win: Window, pathParam: string, href: string): string {\n\tconst params = readHashParams(win);\n\tparams.set(pathParam, href);\n\treturn `?${params.toString()}`;\n}\n\n/** Extract the router href from a SetLocation/ChangeFragment payload. */\nexport function parseHashPayload(payload: string, pathParam: string): string {\n\tif (payload.startsWith(\"/\")) {\n\t\t// legacy plain-hash payload (old createHashRouter hosts)\n\t\treturn payload;\n\t}\n\treturn new URLSearchParams(payload.replace(/^\\?/, \"\")).get(pathParam) ?? \"/\";\n}\n\n/** Hash-mode SetLocation payload: the hash content IS the location. */\nexport function serializeHashLocation(location: {\n\tpathname: string;\n\tsearch: string;\n\thash: string;\n}): string {\n\treturn `${location.pathname}${location.search}${location.hash}`;\n}\n\n/** Hash-mode payload → router href (normalized to a leading slash). */\nexport function parseHashLocation(payload: string): string {\n\treturn payload.startsWith(\"/\") ? payload : `/${payload}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAAe,KAA8B;CAC5D,OAAO,IAAI,gBAAgB,IAAI,SAAS,KAAK,QAAQ,QAAQ,EAAE,CAAC;AACjE;;;;;;AAOA,SAAgB,iBAAiB,KAAa,WAA2B;CACxE,MAAM,OAAO,IAAI,SAAS;CAC1B,IAAI,KAAK,WAAW,IAAI,GACvB,OAAO,IAAI,gBAAgB,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK;CAE7D,IAAI,KAAK,WAAW,IAAI,GACvB,OAAO,KAAK,MAAM,CAAC;CAEpB,OAAO;AACR;;AAGA,SAAgB,gBAAgB,KAAa,WAAmB,MAAsB;CACrF,MAAM,SAAS,eAAe,GAAG;CACjC,OAAO,IAAI,WAAW,IAAI;CAC1B,OAAO,GAAG,IAAI,SAAS,WAAW,IAAI,SAAS,OAAO,IAAI,OAAO,SAAS;AAC3E;;AAGA,SAAgB,qBAAqB,KAAa,WAAmB,MAAsB;CAC1F,MAAM,SAAS,eAAe,GAAG;CACjC,OAAO,IAAI,WAAW,IAAI;CAC1B,OAAO,IAAI,OAAO,SAAS;AAC5B;;AAGA,SAAgB,iBAAiB,SAAiB,WAA2B;CAC5E,IAAI,QAAQ,WAAW,GAAG,GAEzB,OAAO;CAER,OAAO,IAAI,gBAAgB,QAAQ,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK;AAC1E;;AAGA,SAAgB,sBAAsB,UAI3B;CACV,OAAO,GAAG,SAAS,WAAW,SAAS,SAAS,SAAS;AAC1D;;AAGA,SAAgB,kBAAkB,SAAyB;CAC1D,OAAO,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI;AAChD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/core/loose-route.ts
|
|
2
|
+
/** Read a route out of the router's registered tree without the cast noise. */
|
|
3
|
+
function looseRouteOf(router, routeId) {
|
|
4
|
+
return router.routesById[routeId];
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
export { looseRouteOf };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=loose-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loose-route.js","names":[],"sources":["../../src/core/loose-route.ts"],"sourcesContent":["/**\n * Loosely-typed view of a registered route. `AnyRoute`'s options bag is\n * generic-erased (`any`), so every consumer of `router.routesById` needs\n * the same structural recast — this owns it once.\n */\nexport interface LooseRoute {\n\toptions: { staticData?: { panel?: string; modal?: string } };\n\tfullPath?: string;\n}\n\n/** Read a route out of the router's registered tree without the cast noise. */\nexport function looseRouteOf(\n\trouter: { routesById: unknown },\n\trouteId: string,\n): LooseRoute | undefined {\n\treturn (router.routesById as Record<string, LooseRoute>)[routeId];\n}\n"],"mappings":";;AAWA,SAAgB,aACf,QACA,SACyB;CACzB,OAAQ,OAAO,WAA0C;AAC1D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/core/panels-stack.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Mirror of the browser history stack in terms of VKUI panel ids, indexed
|
|
4
|
+
* by `location.state.__TSR_index` (the absolute position in the history
|
|
5
|
+
* stack). Replaces the old router's key-matching ViewHistory with direct
|
|
6
|
+
* indexing. Pure — the React binding gates writes on the router's
|
|
7
|
+
* `status === "idle"` (the location commit lands a render before match
|
|
8
|
+
* resolution, so a naive write records the stale panel at the new index).
|
|
9
|
+
*/
|
|
10
|
+
export interface PanelsStackState {
|
|
11
|
+
/** panel id per history index (null = unknown entry) */
|
|
12
|
+
entries: Array<string | null>;
|
|
13
|
+
/** current history index */
|
|
14
|
+
cursor: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const initialPanelsStack: () => PanelsStackState;
|
|
17
|
+
/**
|
|
18
|
+
* @param index `location.state.__TSR_index`, or null when the history entry
|
|
19
|
+
* carries no TanStack state (manual hash edit / foreign entry).
|
|
20
|
+
*/
|
|
21
|
+
export declare function applyLocation(prev: PanelsStackState, index: number | null, panel: string): PanelsStackState;
|
|
22
|
+
/**
|
|
23
|
+
* VKUI `View.history` prop: panels in open order. Overlay entries never
|
|
24
|
+
* reach this stack (they live in history.state, not as panels).
|
|
25
|
+
*/
|
|
26
|
+
export declare function panelsHistoryOf(state: PanelsStackState): string[];
|
|
27
|
+
//#endregion
|
|
28
|
+
//# sourceMappingURL=panels-stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels-stack.d.ts","names":[],"sources":["../../src/core/panels-stack.ts"],"mappings":";;;;;;;;;iBASiB;;EAEhB,SAAS;;EAET;;qBAGY,0BAAyB;;;;;wBAStB,cACf,MAAM,kBACN,sBACA,gBACE;;;;;wBA8Ca,gBAAgB,OAAO"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/core/panels-stack.ts
|
|
2
|
+
const initialPanelsStack = () => ({
|
|
3
|
+
entries: [],
|
|
4
|
+
cursor: -1
|
|
5
|
+
});
|
|
6
|
+
/**
|
|
7
|
+
* @param index `location.state.__TSR_index`, or null when the history entry
|
|
8
|
+
* carries no TanStack state (manual hash edit / foreign entry).
|
|
9
|
+
*/
|
|
10
|
+
function applyLocation(prev, index, panel) {
|
|
11
|
+
const at = index ?? prev.cursor + 1;
|
|
12
|
+
if (prev.cursor === -1) {
|
|
13
|
+
const filled = [];
|
|
14
|
+
for (let i = 0; i <= at; i++) filled[i] = i === at ? panel : null;
|
|
15
|
+
return {
|
|
16
|
+
entries: filled,
|
|
17
|
+
cursor: at
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (at > prev.cursor) {
|
|
21
|
+
const truncated = prev.entries.slice(0, prev.cursor + 1);
|
|
22
|
+
const filled = [];
|
|
23
|
+
for (let i = 0; i <= at; i++) filled[i] = i === at ? panel : truncated[i] ?? null;
|
|
24
|
+
return {
|
|
25
|
+
entries: filled,
|
|
26
|
+
cursor: at
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (at === prev.cursor) {
|
|
30
|
+
const entries = [...prev.entries];
|
|
31
|
+
entries[at] = panel;
|
|
32
|
+
return {
|
|
33
|
+
entries,
|
|
34
|
+
cursor: at
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const filled = [];
|
|
38
|
+
for (let i = 0; i <= at; i++) filled[i] = prev.entries[i] ?? null;
|
|
39
|
+
if (filled[at] === null) filled[at] = panel;
|
|
40
|
+
return {
|
|
41
|
+
entries: filled,
|
|
42
|
+
cursor: at
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* VKUI `View.history` prop: panels in open order. Overlay entries never
|
|
47
|
+
* reach this stack (they live in history.state, not as panels).
|
|
48
|
+
*/
|
|
49
|
+
function panelsHistoryOf(state) {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (let i = 0; i <= state.cursor; i++) {
|
|
52
|
+
const panel = state.entries[i];
|
|
53
|
+
if (panel) out.push(panel);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { applyLocation, initialPanelsStack, panelsHistoryOf };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=panels-stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels-stack.js","names":[],"sources":["../../src/core/panels-stack.ts"],"sourcesContent":["/**\n * Mirror of the browser history stack in terms of VKUI panel ids, indexed\n * by `location.state.__TSR_index` (the absolute position in the history\n * stack). Replaces the old router's key-matching ViewHistory with direct\n * indexing. Pure — the React binding gates writes on the router's\n * `status === \"idle\"` (the location commit lands a render before match\n * resolution, so a naive write records the stale panel at the new index).\n */\n\nexport interface PanelsStackState {\n\t/** panel id per history index (null = unknown entry) */\n\tentries: Array<string | null>;\n\t/** current history index */\n\tcursor: number;\n}\n\nexport const initialPanelsStack = (): PanelsStackState => ({\n\tentries: [],\n\tcursor: -1,\n});\n\n/**\n * @param index `location.state.__TSR_index`, or null when the history entry\n * carries no TanStack state (manual hash edit / foreign entry).\n */\nexport function applyLocation(\n\tprev: PanelsStackState,\n\tindex: number | null,\n\tpanel: string,\n): PanelsStackState {\n\tconst at = index ?? prev.cursor + 1;\n\n\tif (prev.cursor === -1) {\n\t\t// init (cold start / reload / deep link): anchor at the real history\n\t\t// index — `__TSR_index` survives reloads in history.state. Earlier\n\t\t// slots are unknown (null) and skipped by panelsHistoryOf.\n\t\tconst filled: Array<string | null> = [];\n\t\tfor (let i = 0; i <= at; i++) {\n\t\t\tfilled[i] = i === at ? panel : null;\n\t\t}\n\t\treturn { entries: filled, cursor: at };\n\t}\n\n\tif (at > prev.cursor) {\n\t\t// push: truncate the forward branch, then place the panel at `at`\n\t\tconst truncated = prev.entries.slice(0, prev.cursor + 1);\n\t\tconst filled: Array<string | null> = [];\n\t\tfor (let i = 0; i <= at; i++) {\n\t\t\tfilled[i] = i === at ? panel : (truncated[i] ?? null);\n\t\t}\n\t\treturn { entries: filled, cursor: at };\n\t}\n\n\tif (at === prev.cursor) {\n\t\t// replace (same slot, new location)\n\t\tconst entries = [...prev.entries];\n\t\tentries[at] = panel;\n\t\treturn { entries, cursor: at };\n\t}\n\n\t// pop (back/forward into a known or unknown earlier slot)\n\tconst filled: Array<string | null> = [];\n\tfor (let i = 0; i <= at; i++) {\n\t\tfilled[i] = prev.entries[i] ?? null;\n\t}\n\tif (filled[at] === null) {\n\t\tfilled[at] = panel;\n\t}\n\treturn { entries: filled, cursor: at };\n}\n\n/**\n * VKUI `View.history` prop: panels in open order. Overlay entries never\n * reach this stack (they live in history.state, not as panels).\n */\nexport function panelsHistoryOf(state: PanelsStackState): string[] {\n\tconst out: string[] = [];\n\tfor (let i = 0; i <= state.cursor; i++) {\n\t\tconst panel = state.entries[i];\n\t\tif (panel) {\n\t\t\tout.push(panel);\n\t\t}\n\t}\n\treturn out;\n}\n"],"mappings":";AAgBA,MAAa,4BAA8C;CAC1D,SAAS,CAAC;CACV,QAAQ;AACT;;;;;AAMA,SAAgB,cACf,MACA,OACA,OACmB;CACnB,MAAM,KAAK,SAAS,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,IAAI;EAIvB,MAAM,SAA+B,CAAC;EACtC,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,KACxB,OAAO,KAAK,MAAM,KAAK,QAAQ;EAEhC,OAAO;GAAE,SAAS;GAAQ,QAAQ;EAAG;CACtC;CAEA,IAAI,KAAK,KAAK,QAAQ;EAErB,MAAM,YAAY,KAAK,QAAQ,MAAM,GAAG,KAAK,SAAS,CAAC;EACvD,MAAM,SAA+B,CAAC;EACtC,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,KACxB,OAAO,KAAK,MAAM,KAAK,QAAS,UAAU,MAAM;EAEjD,OAAO;GAAE,SAAS;GAAQ,QAAQ;EAAG;CACtC;CAEA,IAAI,OAAO,KAAK,QAAQ;EAEvB,MAAM,UAAU,CAAC,GAAG,KAAK,OAAO;EAChC,QAAQ,MAAM;EACd,OAAO;GAAE;GAAS,QAAQ;EAAG;CAC9B;CAGA,MAAM,SAA+B,CAAC;CACtC,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,KACxB,OAAO,KAAK,KAAK,QAAQ,MAAM;CAEhC,IAAI,OAAO,QAAQ,MAClB,OAAO,MAAM;CAEd,OAAO;EAAE,SAAS;EAAQ,QAAQ;CAAG;AACtC;;;;;AAMA,SAAgB,gBAAgB,OAAmC;CAClE,MAAM,MAAgB,CAAC;CACvB,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK;EACvC,MAAM,QAAQ,MAAM,QAAQ;EAC5B,IAAI,OACH,IAAI,KAAK,KAAK;CAEhB;CACA,OAAO;AACR"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/core/state-keys.d.ts
|
|
2
|
+
/** The key the popout overlay rides under in `history.state`. */
|
|
3
|
+
export declare const POPOUT_STATE_KEY = "vkma:popout";
|
|
4
|
+
export interface PopoutState {
|
|
5
|
+
id: string;
|
|
6
|
+
props?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export declare function readPopoutState(state: unknown): PopoutState | undefined;
|
|
9
|
+
//#endregion
|
|
10
|
+
//# sourceMappingURL=state-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-keys.d.ts","names":[],"sources":["../../src/core/state-keys.ts"],"mappings":";;qBACa;iBAEI;EAChB;EACA,QAAQ;;wBAqBO,gBAAgB,iBAAiB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/core/state-keys.ts
|
|
2
|
+
/** The key the popout overlay rides under in `history.state`. */
|
|
3
|
+
const POPOUT_STATE_KEY = "vkma:popout";
|
|
4
|
+
/** The key runtime modal props ride under in `history.state`. */
|
|
5
|
+
const MODAL_STATE_KEY = "vkma:modal";
|
|
6
|
+
function readModalState(state) {
|
|
7
|
+
if (state !== null && typeof state === "object" && "vkma:modal" in state) {
|
|
8
|
+
const modal = state[MODAL_STATE_KEY];
|
|
9
|
+
if (modal !== null && typeof modal === "object") return modal;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function readPopoutState(state) {
|
|
13
|
+
if (state !== null && typeof state === "object" && "vkma:popout" in state) {
|
|
14
|
+
const popout = state[POPOUT_STATE_KEY];
|
|
15
|
+
if (popout !== null && typeof popout === "object" && typeof popout.id === "string") return popout;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { MODAL_STATE_KEY, POPOUT_STATE_KEY, readModalState, readPopoutState };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=state-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-keys.js","names":[],"sources":["../../src/core/state-keys.ts"],"sourcesContent":["/** The key the popout overlay rides under in `history.state`. */\nexport const POPOUT_STATE_KEY = \"vkma:popout\";\n\nexport interface PopoutState {\n\tid: string;\n\tprops?: Record<string, unknown>;\n}\n\n/** The key runtime modal props ride under in `history.state`. */\nexport const MODAL_STATE_KEY = \"vkma:modal\";\n\nexport interface ModalState {\n\t/** Serializable props passed via `showModal({ props })`. */\n\tprops?: unknown;\n}\n\nexport function readModalState(state: unknown): ModalState | undefined {\n\tif (state !== null && typeof state === \"object\" && MODAL_STATE_KEY in state) {\n\t\tconst modal = (state as Record<string, unknown>)[MODAL_STATE_KEY];\n\t\tif (modal !== null && typeof modal === \"object\") {\n\t\t\treturn modal;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function readPopoutState(state: unknown): PopoutState | undefined {\n\tif (state !== null && typeof state === \"object\" && POPOUT_STATE_KEY in state) {\n\t\tconst popout = (state as Record<string, unknown>)[POPOUT_STATE_KEY];\n\t\tif (\n\t\t\tpopout !== null &&\n\t\t\ttypeof popout === \"object\" &&\n\t\t\ttypeof (popout as PopoutState).id === \"string\"\n\t\t) {\n\t\t\treturn popout as PopoutState;\n\t\t}\n\t}\n\treturn undefined;\n}\n"],"mappings":";;AACA,MAAa,mBAAmB;;AAQhC,MAAa,kBAAkB;AAO/B,SAAgB,eAAe,OAAwC;CACtE,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAA,gBAA+B,OAAO;EAC5E,MAAM,QAAS,MAAkC;EACjD,IAAI,UAAU,QAAQ,OAAO,UAAU,UACtC,OAAO;CAET;AAED;AAEA,SAAgB,gBAAgB,OAAyC;CACxE,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAA,iBAAgC,OAAO;EAC7E,MAAM,SAAU,MAAkC;EAClD,IACC,WAAW,QACX,OAAO,WAAW,YAClB,OAAQ,OAAuB,OAAO,UAEtC,OAAO;CAET;AAED"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { VkBridgeLike } from "./bridge-like.js";
|
|
2
|
+
//#region src/core/swipe-back.d.ts
|
|
3
|
+
export interface SwipeBackManagerOptions {
|
|
4
|
+
bridge: VkBridgeLike;
|
|
5
|
+
history: {
|
|
6
|
+
location: {
|
|
7
|
+
state?: {
|
|
8
|
+
__TSR_index?: number;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
subscribe(listener: () => void): () => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Hard opt-out: the native gesture is never enabled, whatever the
|
|
15
|
+
* history position or an override says. Default `true`.
|
|
16
|
+
*/
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function createSwipeBackManager(options: SwipeBackManagerOptions): {
|
|
20
|
+
destroy: () => void;
|
|
21
|
+
/** Manual override — `null` returns to the automatic decision. */
|
|
22
|
+
setOverride: (enabled: boolean | null) => void;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
//# sourceMappingURL=swipe-back.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipe-back.d.ts","names":[],"sources":["../../src/core/swipe-back.ts"],"mappings":";;iBA0BiB;EAChB,QAAQ;EACR;IACC;MAAY;QAAU;;;IACtB,UAAU;;;;;;EAMX;;wBAGe,uBAAuB,SAAS;EAC/C;;EAEA,cAAc"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/core/swipe-back.ts
|
|
2
|
+
/**
|
|
3
|
+
* Auto swipe-back management: the native gesture
|
|
4
|
+
* (`VKWebAppEnableSwipeBack` / `VKWebAppSetSwipeSettings { history }`) is
|
|
5
|
+
* enabled while the app can go back in its own history and disabled on the
|
|
6
|
+
* first entry — where the system back must close the app. Unsupported
|
|
7
|
+
* platforms (web) answer with error code 6; that is silenced, like the old
|
|
8
|
+
* router did.
|
|
9
|
+
*
|
|
10
|
+
* Two manual layers on top of the automatic decision:
|
|
11
|
+
* - `enabled: false` is a hard opt-out — the gesture is never enabled
|
|
12
|
+
* (an app that drives the gesture itself, or none at all);
|
|
13
|
+
* - `setOverride(boolean)` — fed by `useEnableSwipeBack` — replaces the
|
|
14
|
+
* automatic decision while set; `null` returns to it.
|
|
15
|
+
*/
|
|
16
|
+
/** The one active manager — `useEnableSwipeBack` talks to the last created
|
|
17
|
+
* one (a mini app has exactly one history). `destroy` unregisters. */
|
|
18
|
+
let activeManager = null;
|
|
19
|
+
/** Manual override slot behind `useEnableSwipeBack`. */
|
|
20
|
+
function setSwipeBackOverride(enabled) {
|
|
21
|
+
activeManager?.setOverride(enabled);
|
|
22
|
+
}
|
|
23
|
+
function createSwipeBackManager(options) {
|
|
24
|
+
let lastEnabled = null;
|
|
25
|
+
let override = null;
|
|
26
|
+
const sync = () => {
|
|
27
|
+
const index = options.history.location.state?.__TSR_index;
|
|
28
|
+
const auto = index === void 0 ? false : index > 0;
|
|
29
|
+
const enabled = options.enabled === false ? false : override ?? auto;
|
|
30
|
+
if (enabled === lastEnabled) return;
|
|
31
|
+
lastEnabled = enabled;
|
|
32
|
+
options.bridge.send("VKWebAppSetSwipeSettings", { history: enabled }).catch(() => {});
|
|
33
|
+
options.bridge.send(enabled ? "VKWebAppEnableSwipeBack" : "VKWebAppDisableSwipeBack").catch(() => {});
|
|
34
|
+
};
|
|
35
|
+
sync();
|
|
36
|
+
const unsubscribe = options.history.subscribe(sync);
|
|
37
|
+
const manager = { setOverride: (enabled) => {
|
|
38
|
+
override = enabled;
|
|
39
|
+
sync();
|
|
40
|
+
} };
|
|
41
|
+
activeManager = manager;
|
|
42
|
+
return {
|
|
43
|
+
setOverride: manager.setOverride,
|
|
44
|
+
destroy: () => {
|
|
45
|
+
unsubscribe();
|
|
46
|
+
if (activeManager === manager) activeManager = null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { createSwipeBackManager, setSwipeBackOverride };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=swipe-back.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipe-back.js","names":[],"sources":["../../src/core/swipe-back.ts"],"sourcesContent":["import type { VkBridgeLike } from \"./bridge-like.ts\";\n\n/**\n * Auto swipe-back management: the native gesture\n * (`VKWebAppEnableSwipeBack` / `VKWebAppSetSwipeSettings { history }`) is\n * enabled while the app can go back in its own history and disabled on the\n * first entry — where the system back must close the app. Unsupported\n * platforms (web) answer with error code 6; that is silenced, like the old\n * router did.\n *\n * Two manual layers on top of the automatic decision:\n * - `enabled: false` is a hard opt-out — the gesture is never enabled\n * (an app that drives the gesture itself, or none at all);\n * - `setOverride(boolean)` — fed by `useEnableSwipeBack` — replaces the\n * automatic decision while set; `null` returns to it.\n */\n\n/** The one active manager — `useEnableSwipeBack` talks to the last created\n * one (a mini app has exactly one history). `destroy` unregisters. */\nlet activeManager: { setOverride(enabled: boolean | null): void } | null = null;\n\n/** Manual override slot behind `useEnableSwipeBack`. */\nexport function setSwipeBackOverride(enabled: boolean | null): void {\n\tactiveManager?.setOverride(enabled);\n}\n\nexport interface SwipeBackManagerOptions {\n\tbridge: VkBridgeLike;\n\thistory: {\n\t\tlocation: { state?: { __TSR_index?: number } };\n\t\tsubscribe(listener: () => void): () => void;\n\t};\n\t/**\n\t * Hard opt-out: the native gesture is never enabled, whatever the\n\t * history position or an override says. Default `true`.\n\t */\n\tenabled?: boolean;\n}\n\nexport function createSwipeBackManager(options: SwipeBackManagerOptions): {\n\tdestroy: () => void;\n\t/** Manual override — `null` returns to the automatic decision. */\n\tsetOverride: (enabled: boolean | null) => void;\n} {\n\tlet lastEnabled: boolean | null = null;\n\tlet override: boolean | null = null;\n\n\tconst sync = (): void => {\n\t\tconst index = options.history.location.state?.__TSR_index;\n\t\tconst auto = index === undefined ? false : index > 0;\n\t\tconst enabled = options.enabled === false ? false : (override ?? auto);\n\t\tif (enabled === lastEnabled) {\n\t\t\treturn;\n\t\t}\n\t\tlastEnabled = enabled;\n\t\tvoid options.bridge.send(\"VKWebAppSetSwipeSettings\", { history: enabled }).catch(() => {});\n\t\tvoid options.bridge\n\t\t\t.send(enabled ? \"VKWebAppEnableSwipeBack\" : \"VKWebAppDisableSwipeBack\")\n\t\t\t.catch(() => {});\n\t};\n\n\tsync();\n\tconst unsubscribe = options.history.subscribe(sync);\n\tconst manager = {\n\t\tsetOverride: (enabled: boolean | null): void => {\n\t\t\toverride = enabled;\n\t\t\tsync();\n\t\t},\n\t};\n\tactiveManager = manager;\n\treturn {\n\t\tsetOverride: manager.setOverride,\n\t\tdestroy: () => {\n\t\t\tunsubscribe();\n\t\t\tif (activeManager === manager) {\n\t\t\t\tactiveManager = null;\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,IAAI,gBAAuE;;AAG3E,SAAgB,qBAAqB,SAA+B;CACnE,eAAe,YAAY,OAAO;AACnC;AAeA,SAAgB,uBAAuB,SAIrC;CACD,IAAI,cAA8B;CAClC,IAAI,WAA2B;CAE/B,MAAM,aAAmB;EACxB,MAAM,QAAQ,QAAQ,QAAQ,SAAS,OAAO;EAC9C,MAAM,OAAO,UAAU,KAAA,IAAY,QAAQ,QAAQ;EACnD,MAAM,UAAU,QAAQ,YAAY,QAAQ,QAAS,YAAY;EACjE,IAAI,YAAY,aACf;EAED,cAAc;EACd,QAAa,OAAO,KAAK,4BAA4B,EAAE,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EACzF,QAAa,OACX,KAAK,UAAU,4BAA4B,0BAA0B,CAAC,CACtE,YAAY,CAAC,CAAC;CACjB;CAEA,KAAK;CACL,MAAM,cAAc,QAAQ,QAAQ,UAAU,IAAI;CAClD,MAAM,UAAU,EACf,cAAc,YAAkC;EAC/C,WAAW;EACX,KAAK;CACN,EACD;CACA,gBAAgB;CAChB,OAAO;EACN,aAAa,QAAQ;EACrB,eAAe;GACd,YAAY;GACZ,IAAI,kBAAkB,SACrB,gBAAgB;EAElB;CACD;AACD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/core/vk-history.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* VK history: the same trick TanStack's own hash history uses —
|
|
4
|
+
* `createBrowserHistory` with swapped `parseLocation`/`createHref` — so we
|
|
5
|
+
* inherit blockers with rollback, popstate classification, throttling and
|
|
6
|
+
* TSR index/key stamping.
|
|
7
|
+
*
|
|
8
|
+
* Modes (parity with the old router's three factories):
|
|
9
|
+
* - `"hash"` (default) → old `createHashRouter`: the hash IS the location
|
|
10
|
+
* (`#/item/7?tab=x`).
|
|
11
|
+
* - `"hash-param"` → old `createHashParamRouter`: the whole router
|
|
12
|
+
* location rides in one hash query param (`#?path=%2Fitem%2F7`), foreign
|
|
13
|
+
* hash params (launch params, UTM) are preserved next to it, and the
|
|
14
|
+
* real `location.search` is never touched.
|
|
15
|
+
*
|
|
16
|
+
* Deep links written by the OTHER mode resolve too (legacy tolerance).
|
|
17
|
+
* `"browser"` → pass TanStack's default history instead (omit `history`
|
|
18
|
+
* in createRouter) — old `createBrowserRouter`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEFAULT_PATH_PARAM = "path";
|
|
21
|
+
export type VkHistoryMode = "hash" | "hash-param";
|
|
22
|
+
export interface VkHistoryOptions {
|
|
23
|
+
window?: Window;
|
|
24
|
+
/** Hash query param carrying the router location (hash-param mode). */
|
|
25
|
+
pathParam?: string;
|
|
26
|
+
/** URL mode. Default `"hash"`. */
|
|
27
|
+
mode?: VkHistoryMode;
|
|
28
|
+
}
|
|
29
|
+
/** The history plus the payload codecs the bridge sync layer needs. */
|
|
30
|
+
export declare function createVkHistory(options?: VkHistoryOptions): {
|
|
31
|
+
history: import("@tanstack/history").RouterHistory;
|
|
32
|
+
/** Hash content for a router href — the SetLocation payload. */
|
|
33
|
+
serializePayload: (href: string) => string;
|
|
34
|
+
/** Router href from a SetLocation/ChangeFragment payload. */
|
|
35
|
+
parsePayload: (payload: string) => string;
|
|
36
|
+
/** Router href from the current document hash. */
|
|
37
|
+
parseCurrentHash: () => string;
|
|
38
|
+
};
|
|
39
|
+
export type VkHistory = ReturnType<typeof createVkHistory>;
|
|
40
|
+
//#endregion
|
|
41
|
+
//# sourceMappingURL=vk-history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vk-history.d.ts","names":[],"sources":["../../src/core/vk-history.ts"],"mappings":";;;;;;;;;;;;;;;;;;;qBA4Ba;YAED;iBAQK;EAChB,SAAS;;EAET;;EAEA,OAAO;;;wBAIQ,gBAAgB,UAAS;;;EAqDb,mBAAA;;EAED,eAAA;;;;YA2Cf,YAAY,kBAAkB"}
|