@fluid-app/portal-sdk 0.1.245 → 0.1.246
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.cjs +73 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +73 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
package/dist/index.cjs
CHANGED
|
@@ -1867,6 +1867,68 @@ function SdkCompanySwitcher() {
|
|
|
1867
1867
|
});
|
|
1868
1868
|
}
|
|
1869
1869
|
//#endregion
|
|
1870
|
+
//#region ../core/src/navigation/slug-utils.ts
|
|
1871
|
+
/**
|
|
1872
|
+
* Extract all slugs from a navigation tree, sorted by segment count descending.
|
|
1873
|
+
* Longest slugs first enables greedy prefix matching (e.g. "share/playlists"
|
|
1874
|
+
* is checked before "share").
|
|
1875
|
+
*/
|
|
1876
|
+
function collectNavSlugs(items) {
|
|
1877
|
+
const slugs = [];
|
|
1878
|
+
for (const item of items) {
|
|
1879
|
+
if (item.slug) slugs.push(normalizeSlug(item.slug));
|
|
1880
|
+
for (const child of item.children ?? []) if (child.slug) slugs.push(normalizeSlug(child.slug));
|
|
1881
|
+
}
|
|
1882
|
+
return [...slugs].sort((a, b) => b.split("/").length - a.split("/").length);
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Find the longest registered nav slug that is a prefix of `fullSlug`.
|
|
1886
|
+
* Uses segment-boundary checking to prevent "shop" from matching "shopping".
|
|
1887
|
+
*/
|
|
1888
|
+
function matchSlugPrefix(fullSlug, navSlugs) {
|
|
1889
|
+
const normalized = normalizeSlug(fullSlug);
|
|
1890
|
+
if (!normalized) return void 0;
|
|
1891
|
+
for (const navSlug of navSlugs) {
|
|
1892
|
+
if (normalized === navSlug) return {
|
|
1893
|
+
matchedSlug: navSlug,
|
|
1894
|
+
rest: ""
|
|
1895
|
+
};
|
|
1896
|
+
if (normalized.startsWith(navSlug + "/")) return {
|
|
1897
|
+
matchedSlug: navSlug,
|
|
1898
|
+
rest: normalized.slice(navSlug.length + 1)
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Extract the slug portion from a full pathname by stripping the basePath prefix.
|
|
1904
|
+
* Returns an empty string when the pathname equals the basePath exactly.
|
|
1905
|
+
*
|
|
1906
|
+
* Examples:
|
|
1907
|
+
* extractSlugFromPathname("/contacts/123", "/") → "contacts/123"
|
|
1908
|
+
* extractSlugFromPathname("/portal/contacts", "/portal") → "contacts"
|
|
1909
|
+
* extractSlugFromPathname("/portal", "/portal") → ""
|
|
1910
|
+
* extractSlugFromPathname("/", "/") → ""
|
|
1911
|
+
*/
|
|
1912
|
+
function extractSlugFromPathname(pathname, basePath) {
|
|
1913
|
+
if (basePath === "/") return pathname.replace(/^\//, "");
|
|
1914
|
+
if (pathname.startsWith(basePath)) return pathname.slice(basePath.length).replace(/^\//, "");
|
|
1915
|
+
return pathname.replace(/^\//, "");
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Generate a URL-safe slug from a human-readable name. Mirrors the admin
|
|
1919
|
+
* builder's `generateSlugFromName` so a navigation item authored as
|
|
1920
|
+
* `slugifyName(screen.name)` can be matched back to the screen at runtime
|
|
1921
|
+
* even when the screen's own `slug` is opaque (e.g. `screen-{uuid}`).
|
|
1922
|
+
*/
|
|
1923
|
+
function slugifyName(name) {
|
|
1924
|
+
return name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
1925
|
+
}
|
|
1926
|
+
function isSlugInSection(item, currentSlug, navSlugs) {
|
|
1927
|
+
const baseSlug = matchSlugPrefix(currentSlug, navSlugs)?.matchedSlug ?? normalizeSlug(currentSlug);
|
|
1928
|
+
if (normalizeSlug(item.slug) === baseSlug) return true;
|
|
1929
|
+
return item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ?? false;
|
|
1930
|
+
}
|
|
1931
|
+
//#endregion
|
|
1870
1932
|
//#region src/shell/go-back-or-home.ts
|
|
1871
1933
|
/**
|
|
1872
1934
|
* Navigate one step back in the browser history if anything is on the stack;
|
|
@@ -2082,7 +2144,14 @@ function PageRouter({ currentSlug, currentNavItem, customPages, screens, baseSlu
|
|
|
2082
2144
|
}, [screens]);
|
|
2083
2145
|
const screenBySlug = (0, react.useMemo)(() => {
|
|
2084
2146
|
if (!screens || screens.length === 0) return void 0;
|
|
2085
|
-
|
|
2147
|
+
const map = /* @__PURE__ */ new Map();
|
|
2148
|
+
for (const s of screens) if (s.slug) map.set(s.slug, s);
|
|
2149
|
+
for (const s of screens) {
|
|
2150
|
+
if (!s.name) continue;
|
|
2151
|
+
const nameSlug = slugifyName(s.name);
|
|
2152
|
+
if (nameSlug && !map.has(nameSlug)) map.set(nameSlug, s);
|
|
2153
|
+
}
|
|
2154
|
+
return map;
|
|
2086
2155
|
}, [screens]);
|
|
2087
2156
|
const builderScreen = (0, react.useMemo)(() => {
|
|
2088
2157
|
if (currentNavItem?.screen_id && screenById) {
|
|
@@ -2124,59 +2193,6 @@ function PageRouter({ currentSlug, currentNavItem, customPages, screens, baseSlu
|
|
|
2124
2193
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScreenNotFound, { name: currentNavItem?.label ?? currentSlug });
|
|
2125
2194
|
}
|
|
2126
2195
|
//#endregion
|
|
2127
|
-
//#region ../core/src/navigation/slug-utils.ts
|
|
2128
|
-
/**
|
|
2129
|
-
* Extract all slugs from a navigation tree, sorted by segment count descending.
|
|
2130
|
-
* Longest slugs first enables greedy prefix matching (e.g. "share/playlists"
|
|
2131
|
-
* is checked before "share").
|
|
2132
|
-
*/
|
|
2133
|
-
function collectNavSlugs(items) {
|
|
2134
|
-
const slugs = [];
|
|
2135
|
-
for (const item of items) {
|
|
2136
|
-
if (item.slug) slugs.push(normalizeSlug(item.slug));
|
|
2137
|
-
for (const child of item.children ?? []) if (child.slug) slugs.push(normalizeSlug(child.slug));
|
|
2138
|
-
}
|
|
2139
|
-
return [...slugs].sort((a, b) => b.split("/").length - a.split("/").length);
|
|
2140
|
-
}
|
|
2141
|
-
/**
|
|
2142
|
-
* Find the longest registered nav slug that is a prefix of `fullSlug`.
|
|
2143
|
-
* Uses segment-boundary checking to prevent "shop" from matching "shopping".
|
|
2144
|
-
*/
|
|
2145
|
-
function matchSlugPrefix(fullSlug, navSlugs) {
|
|
2146
|
-
const normalized = normalizeSlug(fullSlug);
|
|
2147
|
-
if (!normalized) return void 0;
|
|
2148
|
-
for (const navSlug of navSlugs) {
|
|
2149
|
-
if (normalized === navSlug) return {
|
|
2150
|
-
matchedSlug: navSlug,
|
|
2151
|
-
rest: ""
|
|
2152
|
-
};
|
|
2153
|
-
if (normalized.startsWith(navSlug + "/")) return {
|
|
2154
|
-
matchedSlug: navSlug,
|
|
2155
|
-
rest: normalized.slice(navSlug.length + 1)
|
|
2156
|
-
};
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
/**
|
|
2160
|
-
* Extract the slug portion from a full pathname by stripping the basePath prefix.
|
|
2161
|
-
* Returns an empty string when the pathname equals the basePath exactly.
|
|
2162
|
-
*
|
|
2163
|
-
* Examples:
|
|
2164
|
-
* extractSlugFromPathname("/contacts/123", "/") → "contacts/123"
|
|
2165
|
-
* extractSlugFromPathname("/portal/contacts", "/portal") → "contacts"
|
|
2166
|
-
* extractSlugFromPathname("/portal", "/portal") → ""
|
|
2167
|
-
* extractSlugFromPathname("/", "/") → ""
|
|
2168
|
-
*/
|
|
2169
|
-
function extractSlugFromPathname(pathname, basePath) {
|
|
2170
|
-
if (basePath === "/") return pathname.replace(/^\//, "");
|
|
2171
|
-
if (pathname.startsWith(basePath)) return pathname.slice(basePath.length).replace(/^\//, "");
|
|
2172
|
-
return pathname.replace(/^\//, "");
|
|
2173
|
-
}
|
|
2174
|
-
function isSlugInSection(item, currentSlug, navSlugs) {
|
|
2175
|
-
const baseSlug = matchSlugPrefix(currentSlug, navSlugs)?.matchedSlug ?? normalizeSlug(currentSlug);
|
|
2176
|
-
if (normalizeSlug(item.slug) === baseSlug) return true;
|
|
2177
|
-
return item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ?? false;
|
|
2178
|
-
}
|
|
2179
|
-
//#endregion
|
|
2180
2196
|
//#region src/shell/AppShellErrorBoundary.tsx
|
|
2181
2197
|
var AppShellErrorBoundary = class extends react.Component {
|
|
2182
2198
|
state = { error: null };
|
|
@@ -2430,7 +2446,7 @@ function AppShell({ appData: appDataProp, navigation: navigationProp, customPage
|
|
|
2430
2446
|
const navSlugs = (0, react.useMemo)(() => collectNavSlugs(filteredNavItems), [filteredNavItems]);
|
|
2431
2447
|
const allNavSlugs = (0, react.useMemo)(() => {
|
|
2432
2448
|
const mobileSlugs = collectNavSlugs(filteredMobileNavItems);
|
|
2433
|
-
return Array.from(new Set([...navSlugs, ...mobileSlugs]));
|
|
2449
|
+
return Array.from(new Set([...navSlugs, ...mobileSlugs])).sort((a, b) => b.split("/").length - a.split("/").length);
|
|
2434
2450
|
}, [navSlugs, filteredMobileNavItems]);
|
|
2435
2451
|
const [themeMode, setThemeMode] = (0, react.useState)(getInitialThemeMode);
|
|
2436
2452
|
const handleThemeModeChange = (0, react.useCallback)((mode) => {
|
|
@@ -2536,11 +2552,11 @@ function AppShell({ appData: appDataProp, navigation: navigationProp, customPage
|
|
|
2536
2552
|
if (typeof orderToken === "string" && orderToken) handleNavigate(`orders/${orderToken}`);
|
|
2537
2553
|
}, [handleNavigate]);
|
|
2538
2554
|
const resolvedSidebarFooter = (0, react.useMemo)(() => sidebarFooter !== void 0 ? sidebarFooter : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SdkLogoutButton, { onLogout: logout }), [sidebarFooter, logout]);
|
|
2539
|
-
const slugMatch = (0, react.useMemo)(() => matchSlugPrefix(activeSlug,
|
|
2555
|
+
const slugMatch = (0, react.useMemo)(() => matchSlugPrefix(activeSlug, allNavSlugs), [activeSlug, allNavSlugs]);
|
|
2540
2556
|
const baseSlug = slugMatch?.matchedSlug ?? normalizeSlug(activeSlug);
|
|
2541
2557
|
const restParams = slugMatch?.rest ?? "";
|
|
2542
2558
|
const mobileSecondLevelTabs = findCurrentSection(filteredMobileNavItems, baseSlug)?.children ?? [];
|
|
2543
|
-
const currentNavItem = findNavItem(filteredNavItems, baseSlug);
|
|
2559
|
+
const currentNavItem = findNavItem(filteredNavItems, baseSlug) ?? findNavItem(filteredMobileNavItems, baseSlug);
|
|
2544
2560
|
const screenTitle = currentNavItem?.label || screens?.find((s) => s.id === currentNavItem?.screen_id)?.name || void 0;
|
|
2545
2561
|
const content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AppShellErrorBoundary, { children: typeof children === "function" ? children({
|
|
2546
2562
|
currentSlug: activeSlug,
|