@acorex/platform 20.9.12 → 20.9.13
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/fesm2022/acorex-platform-common.mjs +86 -6
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +54 -0
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-default.mjs +12 -3
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-layout-entity.d.ts +16 -1
|
@@ -3001,6 +3001,8 @@ function axpReplaceEntityListRouteQueryParams(router, queryParams) {
|
|
|
3001
3001
|
//#endregion
|
|
3002
3002
|
|
|
3003
3003
|
const DEFAULT_TRAILING_SEGMENTS = ['list', 'view', 'edit', 'create'];
|
|
3004
|
+
/** Query params managed by entity list UI; ignored when matching generic (no-query) menu paths. */
|
|
3005
|
+
const ENTITY_LIST_UI_QUERY_KEYS = new Set(['view']);
|
|
3004
3006
|
//#region ---- Entity Route Helpers ----
|
|
3005
3007
|
/**
|
|
3006
3008
|
* Parses `/{app}/m/{module}/e/{entity}/...` routes into a stable entity key.
|
|
@@ -3021,15 +3023,89 @@ function axpParseEntityRouteKey(segments) {
|
|
|
3021
3023
|
function axpEntityRouteKeysEqual(left, right) {
|
|
3022
3024
|
return left.app === right.app && left.module === right.module && left.entity === right.entity;
|
|
3023
3025
|
}
|
|
3026
|
+
function axpParseQueryString(queryString) {
|
|
3027
|
+
return new URLSearchParams(queryString);
|
|
3028
|
+
}
|
|
3029
|
+
function axpMenuDeclaredNavigationParams(queryString) {
|
|
3030
|
+
const params = axpParseQueryString(queryString);
|
|
3031
|
+
const navigationParams = new URLSearchParams();
|
|
3032
|
+
params.forEach((value, key) => {
|
|
3033
|
+
if (!ENTITY_LIST_UI_QUERY_KEYS.has(key)) {
|
|
3034
|
+
navigationParams.append(key, value);
|
|
3035
|
+
}
|
|
3036
|
+
});
|
|
3037
|
+
return navigationParams;
|
|
3038
|
+
}
|
|
3039
|
+
function axpMenuHasDeclaredNavigationParams(itemQueryString) {
|
|
3040
|
+
return axpMenuDeclaredNavigationParams(itemQueryString).toString().length > 0;
|
|
3041
|
+
}
|
|
3042
|
+
/**
|
|
3043
|
+
* True when every navigation query param declared on the menu path equals the current URL.
|
|
3044
|
+
*/
|
|
3045
|
+
function axpMenuDeclaredQueryParamsMatch(itemQueryString, currentQueryString) {
|
|
3046
|
+
const itemParams = axpMenuDeclaredNavigationParams(itemQueryString);
|
|
3047
|
+
const declaredPairs = [];
|
|
3048
|
+
itemParams.forEach((value, key) => {
|
|
3049
|
+
declaredPairs.push([key, value]);
|
|
3050
|
+
});
|
|
3051
|
+
if (declaredPairs.length === 0) {
|
|
3052
|
+
return axpGenericEntityListMenuQueryParamsMatch(currentQueryString);
|
|
3053
|
+
}
|
|
3054
|
+
const currentParams = axpParseQueryString(currentQueryString);
|
|
3055
|
+
return declaredPairs.every(([key, value]) => currentParams.get(key) === value);
|
|
3056
|
+
}
|
|
3057
|
+
/**
|
|
3058
|
+
* Generic entity list menu items (no declared query params) match only when the URL has no
|
|
3059
|
+
* navigation query params beyond UI-managed keys such as `view`.
|
|
3060
|
+
*/
|
|
3061
|
+
function axpGenericEntityListMenuQueryParamsMatch(currentQueryString) {
|
|
3062
|
+
const currentParams = axpParseQueryString(currentQueryString);
|
|
3063
|
+
let hasNavigationParam = false;
|
|
3064
|
+
currentParams.forEach((_value, key) => {
|
|
3065
|
+
if (!ENTITY_LIST_UI_QUERY_KEYS.has(key)) {
|
|
3066
|
+
hasNavigationParam = true;
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3069
|
+
return !hasNavigationParam;
|
|
3070
|
+
}
|
|
3071
|
+
/**
|
|
3072
|
+
* Counts declared menu query params that match the current URL (for best-match selection).
|
|
3073
|
+
*/
|
|
3074
|
+
function axpMenuQueryParamSpecificity(itemPath, currentPath) {
|
|
3075
|
+
const itemQueryString = itemPath.split('#')[0].split('?')[1] ?? '';
|
|
3076
|
+
const currentQueryString = currentPath.split('#')[0].split('?')[1] ?? '';
|
|
3077
|
+
const itemParams = axpMenuDeclaredNavigationParams(itemQueryString);
|
|
3078
|
+
const currentParams = axpParseQueryString(currentQueryString);
|
|
3079
|
+
let specificity = 0;
|
|
3080
|
+
itemParams.forEach((value, key) => {
|
|
3081
|
+
if (currentParams.get(key) === value) {
|
|
3082
|
+
specificity++;
|
|
3083
|
+
}
|
|
3084
|
+
});
|
|
3085
|
+
return specificity;
|
|
3086
|
+
}
|
|
3024
3087
|
/**
|
|
3025
3088
|
* Matches entity list menu entries against list/detail/create/edit routes for the same entity.
|
|
3026
3089
|
*/
|
|
3027
|
-
function axpTryMatchEntityMenuRoute(itemSegments, pathSegments, currentBase) {
|
|
3090
|
+
function axpTryMatchEntityMenuRoute(itemSegments, pathSegments, currentBase, itemQueryString, currentQueryString) {
|
|
3028
3091
|
const menuEntity = axpParseEntityRouteKey(itemSegments);
|
|
3029
3092
|
const currentEntity = axpParseEntityRouteKey(pathSegments);
|
|
3030
3093
|
if (!menuEntity || !currentEntity || !axpEntityRouteKeysEqual(menuEntity, currentEntity)) {
|
|
3031
3094
|
return null;
|
|
3032
3095
|
}
|
|
3096
|
+
const itemHasDeclaredParams = axpMenuHasDeclaredNavigationParams(itemQueryString);
|
|
3097
|
+
if (itemHasDeclaredParams) {
|
|
3098
|
+
if (!axpIsEntityListPath(currentBase)) {
|
|
3099
|
+
return { isMatch: false, isPartial: false };
|
|
3100
|
+
}
|
|
3101
|
+
if (!axpMenuDeclaredQueryParamsMatch(itemQueryString, currentQueryString)) {
|
|
3102
|
+
return { isMatch: false, isPartial: false };
|
|
3103
|
+
}
|
|
3104
|
+
return { isMatch: true, isPartial: false };
|
|
3105
|
+
}
|
|
3106
|
+
if (axpIsEntityListPath(currentBase) && !axpGenericEntityListMenuQueryParamsMatch(currentQueryString)) {
|
|
3107
|
+
return { isMatch: false, isPartial: false };
|
|
3108
|
+
}
|
|
3033
3109
|
const isPartial = pathSegments.length > menuEntity.entityEndIndex ||
|
|
3034
3110
|
axpIsEntityDetailsPath(currentBase) ||
|
|
3035
3111
|
!axpIsEntityListPath(currentBase);
|
|
@@ -3044,10 +3120,10 @@ function axpMenuPathsMatch(itemPath, currentPath, trailingSegmentsToIgnore = DEF
|
|
|
3044
3120
|
const itemNoHash = itemPath.split('#')[0];
|
|
3045
3121
|
const currentNoHash = currentPath.split('#')[0];
|
|
3046
3122
|
const [itemBase, itemQueryString = ''] = itemNoHash.split('?');
|
|
3047
|
-
const [currentBase] = currentNoHash.split('?');
|
|
3123
|
+
const [currentBase, currentQueryString = ''] = currentNoHash.split('?');
|
|
3048
3124
|
const itemSegments = itemBase.split('/').filter((segment) => segment.length > 0);
|
|
3049
3125
|
const pathSegments = currentBase.split('/').filter((segment) => segment.length > 0);
|
|
3050
|
-
const entityMatch = axpTryMatchEntityMenuRoute(itemSegments, pathSegments, `/${currentBase}
|
|
3126
|
+
const entityMatch = axpTryMatchEntityMenuRoute(itemSegments, pathSegments, `/${currentBase}`, itemQueryString, currentQueryString);
|
|
3051
3127
|
if (entityMatch) {
|
|
3052
3128
|
return entityMatch;
|
|
3053
3129
|
}
|
|
@@ -3072,8 +3148,8 @@ function axpMenuPathsMatch(itemPath, currentPath, trailingSegmentsToIgnore = DEF
|
|
|
3072
3148
|
}
|
|
3073
3149
|
const isPartialMatch = effectiveMenuLength < pathLength;
|
|
3074
3150
|
const isExactMatch = effectiveMenuLength === pathLength;
|
|
3075
|
-
const itemParams =
|
|
3076
|
-
const currentParams =
|
|
3151
|
+
const itemParams = axpParseQueryString(itemQueryString);
|
|
3152
|
+
const currentParams = axpParseQueryString(currentQueryString);
|
|
3077
3153
|
let queryMatches = true;
|
|
3078
3154
|
const itemParamPairs = [];
|
|
3079
3155
|
itemParams.forEach((value, key) => {
|
|
@@ -3098,6 +3174,7 @@ function axpMenuPathsMatch(itemPath, currentPath, trailingSegmentsToIgnore = DEF
|
|
|
3098
3174
|
function axpFindMenuItemByRoute(items, currentPath, trailingSegmentsToIgnore = DEFAULT_TRAILING_SEGMENTS) {
|
|
3099
3175
|
let bestMatch = null;
|
|
3100
3176
|
let bestEffectiveLength = -1;
|
|
3177
|
+
let bestQuerySpecificity = -1;
|
|
3101
3178
|
const visit = (menuItems, ancestors) => {
|
|
3102
3179
|
for (const item of menuItems) {
|
|
3103
3180
|
if (item.children?.length) {
|
|
@@ -3122,8 +3199,11 @@ function axpFindMenuItemByRoute(items, currentPath, trailingSegmentsToIgnore = D
|
|
|
3122
3199
|
if (menuEntity) {
|
|
3123
3200
|
effectiveMenuLength = menuEntity.entityEndIndex;
|
|
3124
3201
|
}
|
|
3125
|
-
|
|
3202
|
+
const querySpecificity = axpMenuQueryParamSpecificity(item.path, currentPath);
|
|
3203
|
+
if (effectiveMenuLength > bestEffectiveLength ||
|
|
3204
|
+
(effectiveMenuLength === bestEffectiveLength && querySpecificity > bestQuerySpecificity)) {
|
|
3126
3205
|
bestEffectiveLength = effectiveMenuLength;
|
|
3206
|
+
bestQuerySpecificity = querySpecificity;
|
|
3127
3207
|
bestMatch = { item, ancestors, isPartialMatch: matchResult.isPartial };
|
|
3128
3208
|
}
|
|
3129
3209
|
}
|