@pnkx-lib/ui 1.9.449 → 1.9.451

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.
@@ -54,31 +54,49 @@ function validateAbsolutePaths(menu, nearestAncestorPath) {
54
54
  }
55
55
  }
56
56
 
57
- const pathToRegex = (pattern) => {
58
- const normalized = normalize(pattern);
59
- const escaped = normalized.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
60
- const withParams = escaped.replace(/:([^/]+)/g, "[^/]+");
61
- return new RegExp(`^${withParams}/?$`);
62
- };
63
- const full = (itemPath) => normalize(itemPath ?? "/");
64
- const matchPath = (configPath, pathname) => pathToRegex(configPath).test(normalize(pathname));
65
- function extractParams(pattern, pathname) {
57
+ function compilePatternStrict(pattern) {
66
58
  const pat = normalize(pattern);
67
- const path = normalize(pathname);
59
+ const keys = [];
68
60
  const escaped = pat.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
69
- const named = escaped.replace(/:([^/]+)/g, (_, key) => `(?<${key}>[^/]+)`);
70
- const rx = new RegExp(`^${named}/?$`);
71
- const m = rx.exec(path);
72
- return m?.groups ?? {};
61
+ const withCaptures = escaped.replace(/:([^/]+)/g, (_m, raw) => {
62
+ const key = raw.replace(/[\\'"]/g, "");
63
+ keys.push(key);
64
+ return "([^/]+)";
65
+ });
66
+ return { regex: new RegExp(`^${withCaptures}/?$`), keys };
67
+ }
68
+ function matchPathStrict(pattern, pathname) {
69
+ const { regex } = compilePatternStrict(pattern);
70
+ return regex.test(normalize(pathname));
71
+ }
72
+ function compilePatternLoose(pattern) {
73
+ const pat = normalize(pattern);
74
+ const keys = [];
75
+ const escaped = pat.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
76
+ const withCaptures = escaped.replace(/:([^/]+)/g, (_m, raw) => {
77
+ const key = raw.replace(/[\\'"]/g, "");
78
+ keys.push(key);
79
+ return "([^/]+)";
80
+ });
81
+ return { regex: new RegExp(`^${withCaptures}(?:/.*)?$`), keys };
82
+ }
83
+ function extractParamsLoose(pattern, pathname) {
84
+ const { regex, keys } = compilePatternLoose(pattern);
85
+ const m = regex.exec(normalize(pathname));
86
+ if (!m) return {};
87
+ const out = {};
88
+ for (let i = 0; i < keys.length; i++) out[keys[i]] = m[i + 1];
89
+ return out;
73
90
  }
74
- const hasParams = (p) => !!p && /:([^/]+)/.test(p ?? "");
91
+ const hasParams = (p) => !!p && /:([^/]+)/.test(p);
92
+ const full = (itemPath) => normalize(itemPath ?? "/");
75
93
  function findTrail(menu, pathUrl) {
76
94
  const trail = [];
77
95
  const walk = (items) => {
78
96
  for (const item of items) {
79
97
  const fullPath = full(item.path);
80
98
  trail.push({ ...item, fullPath });
81
- if (item.path && matchPath(fullPath, pathUrl)) return true;
99
+ if (item.path && matchPathStrict(fullPath, pathUrl)) return true;
82
100
  if (item.children?.length && walk(item.children)) return true;
83
101
  trail.pop();
84
102
  }
@@ -87,6 +105,20 @@ function findTrail(menu, pathUrl) {
87
105
  walk(menu);
88
106
  return trail;
89
107
  }
108
+ function pickParamsFromNearestAncestor(trail, fromIndex, pathname, patternKeys) {
109
+ for (let j = fromIndex; j >= 0; j--) {
110
+ const p = trail[j].path;
111
+ if (p && /:([^/]+)/.test(p)) {
112
+ const all = extractParamsLoose(p, pathname);
113
+ const picked = {};
114
+ for (const k of patternKeys) {
115
+ if (all[k]) picked[k] = all[k];
116
+ }
117
+ if (Object.keys(picked).length) return picked;
118
+ }
119
+ }
120
+ return {};
121
+ }
90
122
  function useBreadcrumb(menuRouter, customBreadcrumb) {
91
123
  const { pathname } = useLocation();
92
124
  const normalizedPath = React.useMemo(() => normalize(pathname), [pathname]);
@@ -98,10 +130,6 @@ function useBreadcrumb(menuRouter, customBreadcrumb) {
98
130
  () => trail.length ? trail[trail.length - 1] : void 0,
99
131
  [trail]
100
132
  );
101
- const paramsFromLeaf = React.useMemo(
102
- () => leaf?.path ? extractParams(leaf.path, normalizedPath) : {},
103
- [leaf?.path, normalizedPath]
104
- );
105
133
  const filteredTrail = React.useMemo(() => {
106
134
  if (!trail.length || leaf?.isShowBreadcrumb !== true) return [];
107
135
  return trail.filter(
@@ -115,16 +143,37 @@ function useBreadcrumb(menuRouter, customBreadcrumb) {
115
143
  const pattern = node.path ?? node.fullPath;
116
144
  let href;
117
145
  if (pattern) {
118
- href = hasParams(pattern) ? (() => {
146
+ if (hasParams(pattern)) {
147
+ const { keys: requiredKeys } = compilePatternLoose(pattern);
148
+ const extracted = extractParamsLoose(pattern, normalizedPath);
149
+ let paramsForNode = requiredKeys.reduce(
150
+ (acc, k) => {
151
+ if (extracted[k]) acc[k] = extracted[k];
152
+ return acc;
153
+ },
154
+ {}
155
+ );
156
+ if (Object.keys(paramsForNode).length < requiredKeys.length) {
157
+ const fromAncestor = pickParamsFromNearestAncestor(
158
+ filteredTrail,
159
+ i,
160
+ normalizedPath,
161
+ requiredKeys
162
+ );
163
+ paramsForNode = { ...fromAncestor, ...paramsForNode };
164
+ }
119
165
  try {
120
- return generatePath(pattern, paramsFromLeaf);
166
+ href = Object.keys(paramsForNode).length === requiredKeys.length ? generatePath(pattern, paramsForNode) : void 0;
121
167
  } catch {
122
- return void 0;
168
+ href = void 0;
123
169
  }
124
- })() : pattern;
170
+ } else {
171
+ href = pattern;
172
+ }
125
173
  }
126
- const titleNode = isLast ? /* @__PURE__ */ jsx("span", { children: node.name }) : href ? /* @__PURE__ */ jsx(Link, { to: href, children: node.name }) : /* @__PURE__ */ jsx("span", { children: node.name });
127
- return { title: titleNode };
174
+ return {
175
+ title: isLast ? /* @__PURE__ */ jsx("span", { children: node.name }) : href ? /* @__PURE__ */ jsx(Link, { to: href, children: node.name }) : /* @__PURE__ */ jsx("span", { children: node.name })
176
+ };
128
177
  });
129
178
  return [
130
179
  ...items,
package/es/index.js CHANGED
@@ -84,7 +84,7 @@ export { Cascader } from './fields/CascaderField.js';
84
84
  export { InputRangePicker } from './fields/InputRangePicker.js';
85
85
  export { useToast } from './hooks/useToast.js';
86
86
  export { useMessage } from './hooks/useMessage.js';
87
- export { c as createComponentWithProps, e as extractRoutesFromMenu, n as normalize, v as validateAbsolutePaths } from './chunks/useBreadcrumb-CQA2mBvb.js';
87
+ export { c as createComponentWithProps, e as extractRoutesFromMenu, n as normalize, v as validateAbsolutePaths } from './chunks/useBreadcrumb-DEKhv1-Z.js';
88
88
 
89
89
  const generateId = (name, path) => {
90
90
  const source = `${name}-${path || ""}`;
@@ -1,7 +1,7 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { r } from '../chunks/index-t0ynpS_n.js';
3
3
  import { Breadcrumb } from './Breadcrumb.js';
4
- import { u as useBreadcrumb } from '../chunks/useBreadcrumb-CQA2mBvb.js';
4
+ import { u as useBreadcrumb } from '../chunks/useBreadcrumb-DEKhv1-Z.js';
5
5
 
6
6
  const BreadcrumbHeading = (props) => {
7
7
  const { menu, customBreadcum } = props;
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { u as useBreadcrumb } from '../chunks/useBreadcrumb-CQA2mBvb.js';
2
+ import { u as useBreadcrumb } from '../chunks/useBreadcrumb-DEKhv1-Z.js';
3
3
  import { Breadcrumb } from './Breadcrumb.js';
4
4
 
5
5
  const WrapperBreadcrumb = ({ menu }) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pnkx-lib/ui",
3
3
  "private": false,
4
- "version": "1.9.449",
4
+ "version": "1.9.451",
5
5
  "type": "module",
6
6
  "main": "./es/index.js",
7
7
  "module": "./es/index.js",