@syntrologie/adapt-nav 2.8.0-canary.7 → 2.8.0-canary.71
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/NavWidget.d.ts +1 -1
- package/dist/NavWidget.d.ts.map +1 -1
- package/dist/NavWidget.js +98 -27
- package/dist/cdn.d.ts +1 -1
- package/dist/cdn.d.ts.map +1 -1
- package/dist/runtime.d.ts +11 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +61 -3
- package/dist/schema.d.ts +402 -188
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +5 -3
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts.map +1 -1
- package/node_modules/@syntro/design-system/dist/tailwind-preset.js +4 -2
- package/node_modules/@syntro/design-system/dist/tokens/colors.css +1 -1
- package/node_modules/@syntro/design-system/dist/tokens/colors.d.ts +2 -2
- package/node_modules/@syntro/design-system/dist/tokens/colors.js +1 -1
- package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts +54 -0
- package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts.map +1 -1
- package/node_modules/@syntro/design-system/dist/tokens/effects.js +44 -0
- package/node_modules/@syntro/design-system/package.json +2 -2
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +1 -1
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +5 -3
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +150 -79
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +266 -67
- package/node_modules/@syntrologie/sdk-contracts/package.json +2 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +8 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +2 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.js +0 -1
- package/node_modules/@syntrologie/shared-editor-ui/package.json +11 -9
- package/package.json +9 -9
package/dist/NavWidget.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare const NavMountableWidget: {
|
|
|
25
25
|
mount(container: HTMLElement, config?: NavConfig & {
|
|
26
26
|
runtime?: NavWidgetRuntime;
|
|
27
27
|
instanceId?: string;
|
|
28
|
-
}): () => void;
|
|
28
|
+
}): (() => void) | undefined;
|
|
29
29
|
};
|
|
30
30
|
export default NavWidget;
|
|
31
31
|
//# sourceMappingURL=NavWidget.d.ts.map
|
package/dist/NavWidget.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavWidget.d.ts","sourceRoot":"","sources":["../src/NavWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"NavWidget.d.ts","sourceRoot":"","sources":["../src/NavWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,KAAK,EAAE,SAAS,EAAgB,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAkVzF;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,cAAc,2CA8NxE;AAMD;;GAEG;AACH,eAAO,MAAM,kBAAkB;qBAEhB,WAAW,WACb,SAAS,GAAG;QAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CA2B3E,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
package/dist/NavWidget.js
CHANGED
|
@@ -11,6 +11,24 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
import { purple, slateGrey } from '@syntro/design-system/tokens';
|
|
12
12
|
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
|
13
13
|
import { createRoot } from 'react-dom/client';
|
|
14
|
+
import { navigateWithFrameworkRouter } from './runtime';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Emoji → Lucide SVG inline mapping (no lucide-react dependency)
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const EMOJI_SVG_MAP = {
|
|
19
|
+
'💵': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="12" x="2" y="6" rx="2"/><circle cx="12" cy="12" r="2"/><path d="M6 12h.01M18 12h.01"/></svg>',
|
|
20
|
+
'🏛️': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" x2="21" y1="22" y2="22"/><line x1="6" x2="6" y1="18" y2="11"/><line x1="10" x2="10" y1="18" y2="11"/><line x1="14" x2="14" y1="18" y2="11"/><line x1="18" x2="18" y1="18" y2="11"/><polygon points="12 2 20 7 4 7"/></svg>',
|
|
21
|
+
'⏭️': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 4 15 12 5 20 5 4"/><line x1="19" x2="19" y1="5" y2="19"/></svg>',
|
|
22
|
+
'➡️': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>',
|
|
23
|
+
'💡': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg>',
|
|
24
|
+
'💰': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="12" x="2" y="6" rx="2"/><circle cx="12" cy="12" r="2"/><path d="M6 12h.01M18 12h.01"/></svg>',
|
|
25
|
+
'📋': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg>',
|
|
26
|
+
'✅': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="m9 11 3 3L22 4"/></svg>',
|
|
27
|
+
'⚠️': '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
|
|
28
|
+
};
|
|
29
|
+
function renderIcon(emoji) {
|
|
30
|
+
return EMOJI_SVG_MAP[emoji] ?? escapeHtml(emoji);
|
|
31
|
+
}
|
|
14
32
|
// ============================================================================
|
|
15
33
|
// Sanitization
|
|
16
34
|
// ============================================================================
|
|
@@ -127,7 +145,8 @@ const themeStyles = {
|
|
|
127
145
|
color: 'var(--sc-content-text-secondary-color)',
|
|
128
146
|
},
|
|
129
147
|
linkButton: {
|
|
130
|
-
|
|
148
|
+
// purple[4] = #6a59ce — design system primary purple, used as fallback when --sc-color-primary is not set
|
|
149
|
+
backgroundColor: `var(--sc-color-primary, ${purple[4]})`,
|
|
131
150
|
color: '#ffffff',
|
|
132
151
|
},
|
|
133
152
|
categoryHeader: {
|
|
@@ -160,7 +179,8 @@ const themeStyles = {
|
|
|
160
179
|
color: 'var(--sc-content-text-secondary-color)',
|
|
161
180
|
},
|
|
162
181
|
linkButton: {
|
|
163
|
-
|
|
182
|
+
// purple[4] = #6a59ce — design system primary purple, used as fallback when --sc-color-primary is not set
|
|
183
|
+
backgroundColor: `var(--sc-color-primary, ${purple[4]})`,
|
|
164
184
|
color: '#ffffff',
|
|
165
185
|
},
|
|
166
186
|
categoryHeader: {
|
|
@@ -171,10 +191,36 @@ const themeStyles = {
|
|
|
171
191
|
},
|
|
172
192
|
},
|
|
173
193
|
};
|
|
174
|
-
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Helpers
|
|
196
|
+
// ============================================================================
|
|
197
|
+
/** Check if any of the given routes match the current page pathname */
|
|
198
|
+
function routeMatchesCurrent(routes) {
|
|
199
|
+
if (typeof window === 'undefined')
|
|
200
|
+
return false;
|
|
201
|
+
const current = window.location.pathname;
|
|
202
|
+
return routes.some((route) => {
|
|
203
|
+
// Strip query/hash from route if present
|
|
204
|
+
const routePath = route.split('?')[0].split('#')[0];
|
|
205
|
+
// Exact match or glob pattern (** suffix)
|
|
206
|
+
if (routePath.endsWith('/**')) {
|
|
207
|
+
return current.startsWith(routePath.slice(0, -3));
|
|
208
|
+
}
|
|
209
|
+
return current === routePath;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/** Apply a brief pulse animation to an element, then remove it */
|
|
213
|
+
function pulseElement(el) {
|
|
214
|
+
const keyframes = [
|
|
215
|
+
{ boxShadow: '0 0 0 0 rgba(13, 148, 136, 0.5)' },
|
|
216
|
+
{ boxShadow: '0 0 0 8px rgba(13, 148, 136, 0)' },
|
|
217
|
+
];
|
|
218
|
+
el.animate(keyframes, { duration: 600, iterations: 3, easing: 'ease-out' });
|
|
219
|
+
}
|
|
220
|
+
function NavTipItem({ item, isExpanded, isLast, onToggle, onNavigate, onFocusAnchor, theme, }) {
|
|
175
221
|
const [isHovered, setIsHovered] = useState(false);
|
|
176
222
|
const colors = themeStyles[theme];
|
|
177
|
-
const { title, description, href, icon, external } = item.config;
|
|
223
|
+
const { title, description, href, icon, external, anchor } = item.config;
|
|
178
224
|
const itemStyle = {
|
|
179
225
|
...baseStyles.item,
|
|
180
226
|
...colors.item,
|
|
@@ -196,14 +242,36 @@ function NavTipItem({ item, isExpanded, isLast, onToggle, onNavigate, theme }) {
|
|
|
196
242
|
maxHeight: isExpanded ? '500px' : '0',
|
|
197
243
|
paddingBottom: isExpanded ? '16px' : '0',
|
|
198
244
|
};
|
|
245
|
+
// Determine the effective navigation target from anchor or legacy href
|
|
246
|
+
const effectiveHref = anchor
|
|
247
|
+
? Array.isArray(anchor.route)
|
|
248
|
+
? anchor.route[0]
|
|
249
|
+
: anchor.route
|
|
250
|
+
: href;
|
|
251
|
+
// Same-page check: anchor exists, selector is meaningful, and route matches current page
|
|
252
|
+
const isSamePage = anchor
|
|
253
|
+
? routeMatchesCurrent(Array.isArray(anchor.route) ? anchor.route : [anchor.route])
|
|
254
|
+
: effectiveHref
|
|
255
|
+
? routeMatchesCurrent([effectiveHref])
|
|
256
|
+
: false;
|
|
257
|
+
const hasSelector = anchor?.selector && anchor.selector !== '*';
|
|
258
|
+
const isFocusAction = isSamePage && hasSelector;
|
|
259
|
+
const hasAction = !!effectiveHref || isFocusAction;
|
|
199
260
|
const handleLinkClick = (e) => {
|
|
200
261
|
e.preventDefault();
|
|
201
262
|
e.stopPropagation();
|
|
202
|
-
if (
|
|
203
|
-
|
|
263
|
+
if (isFocusAction && anchor) {
|
|
264
|
+
onFocusAnchor(anchor);
|
|
265
|
+
}
|
|
266
|
+
else if (effectiveHref) {
|
|
267
|
+
onNavigate(effectiveHref, external ?? false);
|
|
204
268
|
}
|
|
205
269
|
};
|
|
206
|
-
|
|
270
|
+
// CTA label
|
|
271
|
+
const ctaLabel = isFocusAction ? `Focus \u2192` : external ? `Go \u2197` : `Go \u2192`;
|
|
272
|
+
return (_jsxs("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && (
|
|
273
|
+
// biome-ignore lint/security/noDangerouslySetInnerHtml: renderIcon returns sanitized SVG from EMOJI_SVG_MAP or escapeHtml
|
|
274
|
+
_jsx("span", { style: baseStyles.icon, dangerouslySetInnerHTML: { __html: renderIcon(icon) } })), _jsx("span", { children: title }), _jsx("span", { style: chevronStyle, children: '\u203A' })] }), _jsxs("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx("p", { style: baseStyles.description, children: description }), hasAction && (_jsx("a", { href: effectiveHref || '#', onClick: handleLinkClick, style: { ...baseStyles.linkButton, ...colors.linkButton }, target: external ? '_blank' : undefined, rel: external ? 'noopener noreferrer' : undefined, children: ctaLabel }))] })] }));
|
|
207
275
|
}
|
|
208
276
|
// ============================================================================
|
|
209
277
|
// NavWidget Component
|
|
@@ -329,11 +397,32 @@ export function NavWidget({ config, runtime, instanceId }) {
|
|
|
329
397
|
window.open(href, '_blank', 'noopener,noreferrer');
|
|
330
398
|
}
|
|
331
399
|
else {
|
|
400
|
+
// Try the host framework's native router first (Next.js, Nuxt, etc.)
|
|
401
|
+
// Falls back to pushState for vanilla SPAs.
|
|
332
402
|
const url = new URL(href, window.location.origin);
|
|
333
403
|
url.search = window.location.search;
|
|
334
|
-
|
|
404
|
+
if (!navigateWithFrameworkRouter(url.toString())) {
|
|
405
|
+
window.history.pushState(null, '', url.toString());
|
|
406
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
407
|
+
}
|
|
335
408
|
}
|
|
336
409
|
}, [runtime.events, instanceId]);
|
|
410
|
+
// Handle same-page anchor focus: scroll + pulse + focus
|
|
411
|
+
const handleFocusAnchor = useCallback((anchor) => {
|
|
412
|
+
const el = document.querySelector(anchor.selector);
|
|
413
|
+
if (!(el instanceof HTMLElement))
|
|
414
|
+
return;
|
|
415
|
+
runtime.events.publish('nav:tip_focused', {
|
|
416
|
+
instanceId,
|
|
417
|
+
selector: anchor.selector,
|
|
418
|
+
route: anchor.route,
|
|
419
|
+
timestamp: Date.now(),
|
|
420
|
+
});
|
|
421
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
422
|
+
pulseElement(el);
|
|
423
|
+
// Focus after scroll completes
|
|
424
|
+
setTimeout(() => el.focus(), 400);
|
|
425
|
+
}, [runtime.events, instanceId]);
|
|
337
426
|
// Compute container styles
|
|
338
427
|
const containerStyle = {
|
|
339
428
|
...baseStyles.container,
|
|
@@ -348,7 +437,7 @@ export function NavWidget({ config, runtime, instanceId }) {
|
|
|
348
437
|
...themeStyles[resolvedTheme].emptyState,
|
|
349
438
|
};
|
|
350
439
|
// Render a list of nav tip items
|
|
351
|
-
const renderItems = (items) => items.map((tip, index) => (_jsx(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), isLast: index === items.length - 1, onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id)));
|
|
440
|
+
const renderItems = (items) => items.map((tip, index) => (_jsx(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), isLast: index === items.length - 1, onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, onFocusAnchor: handleFocusAnchor, theme: resolvedTheme }, tip.config.id)));
|
|
352
441
|
// Empty state
|
|
353
442
|
if (visibleTips.length === 0) {
|
|
354
443
|
return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: emptyStateStyle, children: "You're all set for now! We'll share helpful tips here when they're relevant to what you're doing." }) }));
|
|
@@ -382,24 +471,6 @@ export const NavMountableWidget = {
|
|
|
382
471
|
root.unmount();
|
|
383
472
|
};
|
|
384
473
|
}
|
|
385
|
-
// HTML fallback for non-React environments
|
|
386
|
-
const tips = navConfig.actions || [];
|
|
387
|
-
container.innerHTML = `
|
|
388
|
-
<div style="font-family: system-ui; max-width: 100%;">
|
|
389
|
-
${tips
|
|
390
|
-
.map((tip) => `
|
|
391
|
-
<div style="margin-bottom: 4px; padding: 12px 16px; background: ${slateGrey[12]}; border-radius: 8px;">
|
|
392
|
-
${tip.config.icon ? `<span>${escapeHtml(tip.config.icon)}</span> ` : ''}<strong>${escapeHtml(tip.config.title)}</strong>
|
|
393
|
-
<p style="margin-top: 8px; color: ${slateGrey[6]}; font-size: 13px;">${escapeHtml(tip.config.description)}</p>
|
|
394
|
-
${tip.config.href ? `<a href="${escapeHtml(tip.config.href)}" style="color: ${purple[2]}; font-size: 13px;">Go →</a>` : ''}
|
|
395
|
-
</div>
|
|
396
|
-
`)
|
|
397
|
-
.join('')}
|
|
398
|
-
</div>
|
|
399
|
-
`;
|
|
400
|
-
return () => {
|
|
401
|
-
container.innerHTML = '';
|
|
402
|
-
};
|
|
403
474
|
},
|
|
404
475
|
};
|
|
405
476
|
export default NavWidget;
|
package/dist/cdn.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare const manifest: {
|
|
|
25
25
|
mount(container: HTMLElement, config?: import("./types").NavConfig & {
|
|
26
26
|
runtime?: import("./types").NavWidgetRuntime;
|
|
27
27
|
instanceId?: string;
|
|
28
|
-
}): () => void;
|
|
28
|
+
}): (() => void) | undefined;
|
|
29
29
|
};
|
|
30
30
|
metadata: {
|
|
31
31
|
name: string;
|
package/dist/cdn.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;2BAsCmymB,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtB7zmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;CAQjD,CAAC;AAaF,eAAe,QAAQ,CAAC"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -10,6 +10,16 @@ import type { ActionExecutor, NavigateAction, ScrollToAction } from './types';
|
|
|
10
10
|
* Execute a scrollTo action
|
|
11
11
|
*/
|
|
12
12
|
export declare const executeScrollTo: ActionExecutor<ScrollToAction>;
|
|
13
|
+
/**
|
|
14
|
+
* Try to navigate using the host framework's native router.
|
|
15
|
+
* Returns true if a framework router handled the navigation,
|
|
16
|
+
* false if no framework was detected (caller should use pushState or location.href).
|
|
17
|
+
*
|
|
18
|
+
* Using the native router preserves SPA behavior (no full reload),
|
|
19
|
+
* layout state, scroll position, and framework-specific features
|
|
20
|
+
* (RSC streaming, view transitions, etc.).
|
|
21
|
+
*/
|
|
22
|
+
export declare function navigateWithFrameworkRouter(url: string): boolean;
|
|
13
23
|
/**
|
|
14
24
|
* Execute a navigate action
|
|
15
25
|
*/
|
|
@@ -56,7 +66,7 @@ export declare const runtime: {
|
|
|
56
66
|
mount(container: HTMLElement, config?: import("./types").NavConfig & {
|
|
57
67
|
runtime?: import("./types").NavWidgetRuntime;
|
|
58
68
|
instanceId?: string;
|
|
59
|
-
}): () => void;
|
|
69
|
+
}): (() => void) | undefined;
|
|
60
70
|
};
|
|
61
71
|
metadata: {
|
|
62
72
|
name: string;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA+B1D,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA+B1D,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAgDhE;AAgBD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAuC1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;EAGZ,CAAC;AAMX;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;IAMlB;;OAEG;;;;;;;;IAGH;;OAEG;;;;;uBAqC6lb,CAAC;0BAA8B,CAAC;;;;;;;;;IAxBhob;;;;OAIG;0BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;CAgB9C,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -36,6 +36,61 @@ export const executeScrollTo = async (action, context) => {
|
|
|
36
36
|
},
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Try to navigate using the host framework's native router.
|
|
41
|
+
* Returns true if a framework router handled the navigation,
|
|
42
|
+
* false if no framework was detected (caller should use pushState or location.href).
|
|
43
|
+
*
|
|
44
|
+
* Using the native router preserves SPA behavior (no full reload),
|
|
45
|
+
* layout state, scroll position, and framework-specific features
|
|
46
|
+
* (RSC streaming, view transitions, etc.).
|
|
47
|
+
*/
|
|
48
|
+
export function navigateWithFrameworkRouter(url) {
|
|
49
|
+
if (typeof window === 'undefined')
|
|
50
|
+
return false;
|
|
51
|
+
const w = window;
|
|
52
|
+
// Next.js �� window.next.router.push() triggers App Router navigation
|
|
53
|
+
// with RSC fetch, layout preservation, and loading states
|
|
54
|
+
try {
|
|
55
|
+
const nextRouter = w.next?.router;
|
|
56
|
+
if (nextRouter?.push) {
|
|
57
|
+
nextRouter.push(url);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* fall through */
|
|
63
|
+
}
|
|
64
|
+
// Nuxt 3 — useRouter() isn't accessible from outside Vue, but
|
|
65
|
+
// $nuxt.$router (Nuxt 2) or window.__NUXT__?.hooks (Nuxt 3) + navigateTo
|
|
66
|
+
// aren't reliably exposed. Nuxt 2 exposes $nuxt.$router.push().
|
|
67
|
+
try {
|
|
68
|
+
if (w.$nuxt?.$router?.push) {
|
|
69
|
+
w.$nuxt.$router.push(url);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
/* fall through */
|
|
75
|
+
}
|
|
76
|
+
// Angular — Angular Router isn't exposed on window by default.
|
|
77
|
+
// The most reliable approach is to detect Angular and use location.href.
|
|
78
|
+
if (w.ng || w.getAllAngularRootElements || document.querySelector('[ng-version]')) {
|
|
79
|
+
window.location.href = url;
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
// SvelteKit — goto() isn't on window, but __SVELTEKIT_DATA__ confirms the framework.
|
|
83
|
+
if (w.__SVELTEKIT_DATA__ || document.body?.hasAttribute('data-sveltekit')) {
|
|
84
|
+
window.location.href = url;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
// Astro (View Transitions) — no client-side router API exposed
|
|
88
|
+
if (document.querySelector('[data-astro-transition-fallback]')) {
|
|
89
|
+
window.location.href = url;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
39
94
|
/**
|
|
40
95
|
* Check if a URL is same-origin as the current page.
|
|
41
96
|
* Relative URLs (e.g. "/dashboard") are always same-origin.
|
|
@@ -71,9 +126,12 @@ export const executeNavigate = async (action, context) => {
|
|
|
71
126
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
72
127
|
}
|
|
73
128
|
else if (!action.forceFullNavigation && isSameOrigin(url)) {
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
129
|
+
// Try the host framework's native router first (Next.js, Nuxt, Angular, etc.)
|
|
130
|
+
// Falls back to pushState for vanilla SPAs, or location.href as last resort.
|
|
131
|
+
if (!navigateWithFrameworkRouter(url)) {
|
|
132
|
+
window.history.pushState(null, '', url);
|
|
133
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
134
|
+
}
|
|
77
135
|
}
|
|
78
136
|
else {
|
|
79
137
|
// Full navigation for cross-origin URLs or when explicitly requested
|