@ionic/core 9.0.9 → 9.0.10-dev.11793383958.19a7d413
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/components/ion-input-otp.js +1 -1
- package/components/ion-menu.js +1 -1
- package/components/ion-router.js +1 -1
- package/dist/cjs/ion-input-otp.cjs.entry.js +126 -10
- package/dist/cjs/ion-menu_3.cjs.entry.js +25 -7
- package/dist/cjs/ion-route_4.cjs.entry.js +121 -16
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/input-otp/input-otp.ios.css +30 -5
- package/dist/collection/components/input-otp/input-otp.js +191 -12
- package/dist/collection/components/input-otp/input-otp.md.css +30 -5
- package/dist/collection/components/menu/menu.js +25 -8
- package/dist/collection/components/route/route.js +9 -1
- package/dist/collection/components/route-redirect/route-redirect.js +1 -1
- package/dist/collection/components/router/utils/constants.js +2 -0
- package/dist/collection/components/router/utils/matching.js +97 -14
- package/dist/collection/components/router/utils/path.js +17 -3
- package/dist/docs.json +92 -3
- package/dist/esm/ion-input-otp.entry.js +128 -12
- package/dist/esm/ion-menu_3.entry.js +26 -8
- package/dist/esm/ion-route_4.entry.js +121 -16
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/html.html-data.json +10 -2
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-095da8ec.entry.js +4 -0
- package/dist/ionic/p-705759fd.entry.js +4 -0
- package/dist/ionic/p-99c518de.entry.js +4 -0
- package/dist/types/components/input-otp/input-otp-interface.d.ts +1 -0
- package/dist/types/components/input-otp/input-otp.d.ts +40 -2
- package/dist/types/components/menu/menu.d.ts +7 -0
- package/dist/types/components/route/route.d.ts +8 -0
- package/dist/types/components/route-redirect/route-redirect.d.ts +3 -1
- package/dist/types/components/router/utils/constants.d.ts +2 -0
- package/dist/types/components/router/utils/matching.d.ts +10 -0
- package/dist/types/components/router/utils/path.d.ts +5 -1
- package/dist/types/components.d.ts +32 -6
- package/hydrate/index.js +257 -33
- package/hydrate/index.mjs +257 -33
- package/package.json +1 -1
- package/dist/ionic/p-539d6c8e.entry.js +0 -4
- package/dist/ionic/p-7ce9c6f8.entry.js +0 -4
- package/dist/ionic/p-af11d3c6.entry.js +0 -4
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
* different selectors for different fill variants.
|
|
112
112
|
*/
|
|
113
113
|
--highlight-color: var(--highlight-color-focused);
|
|
114
|
+
--border-color: initial;
|
|
114
115
|
display: block;
|
|
115
116
|
position: relative;
|
|
116
117
|
font-size: 0.875rem;
|
|
@@ -148,7 +149,7 @@
|
|
|
148
149
|
height: var(--height);
|
|
149
150
|
border-width: var(--border-width);
|
|
150
151
|
border-style: solid;
|
|
151
|
-
border-color: var(--border-color);
|
|
152
|
+
border-color: var(--border-color, var(--internal-border-color));
|
|
152
153
|
background: var(--background);
|
|
153
154
|
color: var(--color);
|
|
154
155
|
font-size: inherit;
|
|
@@ -171,6 +172,30 @@
|
|
|
171
172
|
display: none;
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
.input-otp-bottom {
|
|
176
|
+
font-size: 0.75rem;
|
|
177
|
+
line-height: 1.25rem;
|
|
178
|
+
text-align: center;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.input-otp-bottom .error-text {
|
|
182
|
+
display: none;
|
|
183
|
+
color: var(--highlight-color-invalid);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.input-otp-bottom .helper-text {
|
|
187
|
+
display: block;
|
|
188
|
+
color: var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
:host(.ion-touched.ion-invalid) .input-otp-bottom .error-text {
|
|
192
|
+
display: block;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
:host(.ion-touched.ion-invalid) .input-otp-bottom .helper-text {
|
|
196
|
+
display: none;
|
|
197
|
+
}
|
|
198
|
+
|
|
174
199
|
.input-otp-separator {
|
|
175
200
|
border-radius: var(--separator-border-radius);
|
|
176
201
|
flex-shrink: 0;
|
|
@@ -220,7 +245,7 @@
|
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
:host(.input-otp-fill-solid) {
|
|
223
|
-
--border-color: var(--ion-color-step-50, var(--ion-background-color-step-50, #f2f2f2));
|
|
248
|
+
--internal-border-color: var(--ion-color-step-50, var(--ion-background-color-step-50, #f2f2f2));
|
|
224
249
|
--background: var(--ion-color-step-50, var(--ion-background-color-step-50, #f2f2f2));
|
|
225
250
|
}
|
|
226
251
|
|
|
@@ -230,7 +255,7 @@
|
|
|
230
255
|
|
|
231
256
|
:host(.input-otp-fill-outline.input-otp-disabled) {
|
|
232
257
|
--background: var(--ion-color-step-50, var(--ion-background-color-step-50, #f2f2f2));
|
|
233
|
-
--border-color: var(--ion-color-step-100, var(--ion-background-color-step-100, #e6e6e6));
|
|
258
|
+
--internal-border-color: var(--ion-color-step-100, var(--ion-background-color-step-100, #e6e6e6));
|
|
234
259
|
}
|
|
235
260
|
|
|
236
261
|
:host(.input-otp-disabled),
|
|
@@ -249,7 +274,7 @@
|
|
|
249
274
|
|
|
250
275
|
:host(.input-otp-fill-solid.input-otp-disabled),
|
|
251
276
|
:host(.input-otp-fill-solid.input-otp-readonly) {
|
|
252
|
-
--border-color: var(--ion-color-step-100, var(--ion-background-color-step-100, #e6e6e6));
|
|
277
|
+
--internal-border-color: var(--ion-color-step-100, var(--ion-background-color-step-100, #e6e6e6));
|
|
253
278
|
--background: var(--ion-color-step-100, var(--ion-background-color-step-100, #e6e6e6));
|
|
254
279
|
}
|
|
255
280
|
|
|
@@ -377,5 +402,5 @@
|
|
|
377
402
|
}
|
|
378
403
|
|
|
379
404
|
:host(.input-otp-fill-outline) {
|
|
380
|
-
--border-color: var(--ion-color-step-300, var(--ion-background-color-step-300, #b3b3b3));
|
|
405
|
+
--internal-border-color: var(--ion-color-step-300, var(--ion-background-color-step-300, #b3b3b3));
|
|
381
406
|
}
|
|
@@ -11,7 +11,6 @@ import { printIonError } from "../../utils/logging/index";
|
|
|
11
11
|
import { menuController } from "../../utils/menu-controller/index";
|
|
12
12
|
import { BACKDROP, GESTURE, getPresentedOverlay } from "../../utils/overlays";
|
|
13
13
|
import { isPlatform } from "../../utils/platform";
|
|
14
|
-
import { hostContext } from "../../utils/theme";
|
|
15
14
|
import { config } from "../../global/config";
|
|
16
15
|
import { getIonMode } from "../../global/ionic-global";
|
|
17
16
|
const iosEasing = 'cubic-bezier(0.32,0.72,0,1)';
|
|
@@ -169,7 +168,7 @@ export class Menu {
|
|
|
169
168
|
* of the split pane. If the split pane is
|
|
170
169
|
* hidden then the menu should be too.
|
|
171
170
|
*/
|
|
172
|
-
const splitPane = this.
|
|
171
|
+
const splitPane = this.getSplitPane();
|
|
173
172
|
if (splitPane !== null) {
|
|
174
173
|
this.isPaneVisible = await splitPane.isVisible();
|
|
175
174
|
}
|
|
@@ -208,8 +207,8 @@ export class Menu {
|
|
|
208
207
|
this.contentEl = undefined;
|
|
209
208
|
}
|
|
210
209
|
onSplitPaneChanged(ev) {
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
210
|
+
const splitPane = this.getSplitPane();
|
|
211
|
+
if (splitPane !== null && splitPane === ev.target) {
|
|
213
212
|
this.isPaneVisible = ev.detail.visible;
|
|
214
213
|
this.updateState();
|
|
215
214
|
}
|
|
@@ -407,6 +406,24 @@ export class Menu {
|
|
|
407
406
|
_isActive() {
|
|
408
407
|
return !this.disabled && !this.isPaneVisible;
|
|
409
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Returns the split pane this menu is a side pane of.
|
|
411
|
+
* A menu inside the split pane's main content, such as one
|
|
412
|
+
* in a page, is a regular menu. The menu doesn't need to be
|
|
413
|
+
* a direct child, since it can be wrapped in a component.
|
|
414
|
+
*/
|
|
415
|
+
getSplitPane() {
|
|
416
|
+
const splitPane = this.el.closest('ion-split-pane');
|
|
417
|
+
const main = this.el.closest('.split-pane-main');
|
|
418
|
+
/**
|
|
419
|
+
* A split pane can be the main content of an outer split
|
|
420
|
+
* pane, so only this split pane's main content counts.
|
|
421
|
+
*/
|
|
422
|
+
if (splitPane === null || (main !== null && main !== splitPane && splitPane.contains(main))) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
return splitPane;
|
|
426
|
+
}
|
|
410
427
|
canSwipe() {
|
|
411
428
|
return this.swipeGesture && !this.isAnimating && this._isActive();
|
|
412
429
|
}
|
|
@@ -650,21 +667,21 @@ export class Menu {
|
|
|
650
667
|
}
|
|
651
668
|
}
|
|
652
669
|
render() {
|
|
653
|
-
const { type, disabled,
|
|
670
|
+
const { type, disabled, isPaneVisible, inheritedAttributes, side } = this;
|
|
654
671
|
const mode = getIonMode(this);
|
|
655
672
|
/**
|
|
656
673
|
* If the Close Watcher is enabled then
|
|
657
674
|
* the ionBackButton listener in the menu controller
|
|
658
675
|
* will handle closing the menu when Escape is pressed.
|
|
659
676
|
*/
|
|
660
|
-
return (h(Host, { key: '
|
|
677
|
+
return (h(Host, { key: '7481c8a32a135fb0fd6d5591eecbf3fb0a8e8dde', onKeyDown: shouldUseCloseWatcher() ? null : this.onKeydown, role: "navigation", "aria-label": inheritedAttributes['aria-label'] || 'menu', class: {
|
|
661
678
|
[mode]: true,
|
|
662
679
|
[`menu-type-${type}`]: true,
|
|
663
680
|
'menu-enabled': !disabled,
|
|
664
681
|
[`menu-side-${side}`]: true,
|
|
665
682
|
'menu-pane-visible': isPaneVisible,
|
|
666
|
-
'split-pane-side':
|
|
667
|
-
} }, h("div", { key: '
|
|
683
|
+
'split-pane-side': this.getSplitPane() !== null,
|
|
684
|
+
} }, h("div", { key: '8ac2f0f531db944a46e5fef016d0b18bd7b804cc', class: "menu-inner", part: "container", ref: (el) => (this.menuInnerEl = el) }, h("slot", { key: '1ac3e13ccccf1adb7915b739fc794f2889b626ce' })), h("ion-backdrop", { key: 'f8becbf4de1a13feb57fb87973befecb88510bab', ref: (el) => (this.backdropEl = el), class: "menu-backdrop", tappable: false, stopPropagation: false, part: "backdrop" })));
|
|
668
685
|
}
|
|
669
686
|
static get is() { return "ion-menu"; }
|
|
670
687
|
static get encapsulation() { return "shadow"; }
|
|
@@ -6,6 +6,14 @@ export class Route {
|
|
|
6
6
|
*
|
|
7
7
|
* Accepts paths similar to expressjs so that you can define parameters
|
|
8
8
|
* in the url /foo/:bar where bar would be available in incoming props.
|
|
9
|
+
*
|
|
10
|
+
* A url ending in `*`, such as `*` or `/admin/*`, matches any remaining path,
|
|
11
|
+
* including none, so `/admin/*` also matches `/admin`. It only wins over a
|
|
12
|
+
* route that matches the whole path when it has more fixed segments, so
|
|
13
|
+
* `/admin/*` wins over `/:section/:id` but not over `/admin/:id`. It always
|
|
14
|
+
* wins over a route that matches by ignoring trailing segments, such as
|
|
15
|
+
* `/admin` for `/admin/users`. The matched path is passed in the `pathMatch`
|
|
16
|
+
* prop without a leading `/`. A `*` on a route with child routes is ignored.
|
|
9
17
|
*/
|
|
10
18
|
this.url = '';
|
|
11
19
|
}
|
|
@@ -47,7 +55,7 @@ export class Route {
|
|
|
47
55
|
"optional": false,
|
|
48
56
|
"docs": {
|
|
49
57
|
"tags": [],
|
|
50
|
-
"text": "Relative path that needs to match in order for this route to apply.\n\nAccepts paths similar to expressjs so that you can define parameters\nin the url /foo/:bar where bar would be available in incoming props."
|
|
58
|
+
"text": "Relative path that needs to match in order for this route to apply.\n\nAccepts paths similar to expressjs so that you can define parameters\nin the url /foo/:bar where bar would be available in incoming props.\n\nA url ending in `*`, such as `*` or `/admin/*`, matches any remaining path,\nincluding none, so `/admin/*` also matches `/admin`. It only wins over a\nroute that matches the whole path when it has more fixed segments, so\n`/admin/*` wins over `/:section/:id` but not over `/admin/:id`. It always\nwins over a route that matches by ignoring trailing segments, such as\n`/admin` for `/admin/users`. The matched path is passed in the `pathMatch`\nprop without a leading `/`. A `*` on a route with child routes is ignored."
|
|
51
59
|
},
|
|
52
60
|
"getter": false,
|
|
53
61
|
"setter": false,
|
|
@@ -20,7 +20,7 @@ export class RouteRedirect {
|
|
|
20
20
|
"optional": false,
|
|
21
21
|
"docs": {
|
|
22
22
|
"tags": [],
|
|
23
|
-
"text": "A redirect route, redirects \"from\" a URL \"to\" another URL. This property is that \"from\" URL.\nIt needs to be an exact match of the navigated URL in order to apply
|
|
23
|
+
"text": "A redirect route, redirects \"from\" a URL \"to\" another URL. This property is that \"from\" URL.\nIt needs to be an exact match of the navigated URL in order to apply, unless it contains a `*`.\nA `*` matches the rest of the path from that segment on, as long as the path has at least as\nmany segments as `from`, so `/admin/*` matches `/admin/posts` but not `/admin`.\n\nThe path specified in this value is always an absolute path, even if the initial `/` slash\nis not specified."
|
|
24
24
|
},
|
|
25
25
|
"getter": false,
|
|
26
26
|
"setter": false,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
+
import { WILDCARD_PARAM } from "./constants";
|
|
5
|
+
import { chainToSegments, endsWithWildcard, withoutWildcard } from "./path";
|
|
4
6
|
/**
|
|
5
7
|
* Returns whether the given redirect matches the given path segments.
|
|
6
8
|
*
|
|
@@ -41,6 +43,10 @@ export const matchesIDs = (ids, chain) => {
|
|
|
41
43
|
if (routeId.id.toLowerCase() !== routeChain.id) {
|
|
42
44
|
break;
|
|
43
45
|
}
|
|
46
|
+
// Weight a wildcard route when the outlet reports its `pathMatch`
|
|
47
|
+
if (routeId.params?.[WILDCARD_PARAM] !== undefined && endsWithWildcard(routeChain.segments)) {
|
|
48
|
+
score++;
|
|
49
|
+
}
|
|
44
50
|
if (routeId.params) {
|
|
45
51
|
const routeIdParams = Object.keys(routeId.params);
|
|
46
52
|
// Only compare routes with the chain that have the same number of parameters.
|
|
@@ -96,21 +102,28 @@ export const matchesSegments = (segments, chain) => {
|
|
|
96
102
|
matchesDefault = true;
|
|
97
103
|
}
|
|
98
104
|
else {
|
|
99
|
-
|
|
105
|
+
const params = {};
|
|
106
|
+
for (const segment of withoutWildcard(chainSegments)) {
|
|
100
107
|
const data = inputSegments.next();
|
|
101
108
|
// data param
|
|
102
109
|
if (segment[0] === ':') {
|
|
103
110
|
if (data === '') {
|
|
104
111
|
return null;
|
|
105
112
|
}
|
|
106
|
-
allparams = allparams || [];
|
|
107
|
-
const params = allparams[i] || (allparams[i] = {});
|
|
108
113
|
params[segment.slice(1)] = data;
|
|
109
114
|
}
|
|
110
115
|
else if (data !== segment) {
|
|
111
116
|
return null;
|
|
112
117
|
}
|
|
113
118
|
}
|
|
119
|
+
// A trailing `*` on a parent route has no effect. Its children match the rest of the path.
|
|
120
|
+
if (i === chain.length - 1 && endsWithWildcard(chainSegments)) {
|
|
121
|
+
params[WILDCARD_PARAM] = inputSegments.rest().join('/');
|
|
122
|
+
}
|
|
123
|
+
if (Object.keys(params).length > 0) {
|
|
124
|
+
allparams = allparams || [];
|
|
125
|
+
allparams[i] = params;
|
|
126
|
+
}
|
|
114
127
|
matchesDefault = false;
|
|
115
128
|
}
|
|
116
129
|
}
|
|
@@ -142,25 +155,38 @@ export const mergeParams = (a, b) => {
|
|
|
142
155
|
* Returns the best match or null when no match is found.
|
|
143
156
|
* When a chain is returned the parameters are updated from the RouteIDs.
|
|
144
157
|
* That is they contain both the componentProps of the <ion-route> and the parameter segment.
|
|
158
|
+
*
|
|
159
|
+
* When a wildcard route ties with other routes, a route without a wildcard that can build a url wins,
|
|
160
|
+
* then the wildcard route. Otherwise the first route declared wins a tie.
|
|
145
161
|
*/
|
|
146
162
|
export const findChainForIDs = (ids, chains) => {
|
|
147
|
-
let match = null;
|
|
148
163
|
let maxMatches = 0;
|
|
164
|
+
let tied = [];
|
|
149
165
|
for (const chain of chains) {
|
|
150
166
|
const score = matchesIDs(ids, chain);
|
|
151
167
|
if (score > maxMatches) {
|
|
152
|
-
match = chain;
|
|
153
168
|
maxMatches = score;
|
|
169
|
+
tied = [chain];
|
|
170
|
+
}
|
|
171
|
+
else if (score > 0 && score === maxMatches) {
|
|
172
|
+
tied.push(chain);
|
|
154
173
|
}
|
|
155
174
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
175
|
+
const matches = tied.map((chain) => chain.map((route, i) => ({
|
|
176
|
+
id: route.id,
|
|
177
|
+
segments: route.segments,
|
|
178
|
+
params: mergeParams(route.params, ids[i]?.params),
|
|
179
|
+
})));
|
|
180
|
+
if (!matches.some(chainEndsWithWildcard)) {
|
|
181
|
+
return matches.length > 0 ? matches[0] : null;
|
|
182
|
+
}
|
|
183
|
+
return matches.reduce((best, chain) => (tieRank(chain) > tieRank(best) ? chain : best));
|
|
184
|
+
};
|
|
185
|
+
const tieRank = (chain) => {
|
|
186
|
+
if (chainEndsWithWildcard(chain)) {
|
|
187
|
+
return 1;
|
|
162
188
|
}
|
|
163
|
-
return null;
|
|
189
|
+
return chainToSegments(chain) !== null ? 2 : 0;
|
|
164
190
|
};
|
|
165
191
|
/**
|
|
166
192
|
* Finds the best match for the segments in the chains.
|
|
@@ -168,10 +194,17 @@ export const findChainForIDs = (ids, chains) => {
|
|
|
168
194
|
* Returns the best match or null when no match is found.
|
|
169
195
|
* When a chain is returned the parameters are updated from the segments.
|
|
170
196
|
* That is they contain both the componentProps of the <ion-route> and the parameter segments.
|
|
197
|
+
*
|
|
198
|
+
* When wildcard routes match, the one with the most fixed segments wins if it has more fixed segments than every
|
|
199
|
+
* route that matched the whole path. Routes that only matched by ignoring trailing segments don't count.
|
|
171
200
|
*/
|
|
172
201
|
export const findChainForSegments = (segments, chains) => {
|
|
173
202
|
let match = null;
|
|
174
203
|
let bestScore = 0;
|
|
204
|
+
let fullMatch = null;
|
|
205
|
+
let bestFullScore = 0;
|
|
206
|
+
let maxFullFixed = -1;
|
|
207
|
+
let wildcard = null;
|
|
175
208
|
for (const chain of chains) {
|
|
176
209
|
const matchedChain = matchesSegments(segments, chain);
|
|
177
210
|
if (matchedChain !== null) {
|
|
@@ -180,9 +213,51 @@ export const findChainForSegments = (segments, chains) => {
|
|
|
180
213
|
bestScore = score;
|
|
181
214
|
match = matchedChain;
|
|
182
215
|
}
|
|
216
|
+
if (chainEndsWithWildcard(matchedChain)) {
|
|
217
|
+
if (wildcard === null || winsWildcard(matchedChain, wildcard)) {
|
|
218
|
+
wildcard = matchedChain;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (!isPartialMatch(segments, matchedChain)) {
|
|
222
|
+
if (score > bestFullScore) {
|
|
223
|
+
bestFullScore = score;
|
|
224
|
+
fullMatch = matchedChain;
|
|
225
|
+
}
|
|
226
|
+
maxFullFixed = Math.max(maxFullFixed, countFixedSegments(matchedChain));
|
|
227
|
+
}
|
|
183
228
|
}
|
|
184
229
|
}
|
|
185
|
-
|
|
230
|
+
// Without a wildcard to match the rest of the path, routes can still ignore trailing segments.
|
|
231
|
+
if (wildcard === null) {
|
|
232
|
+
return match;
|
|
233
|
+
}
|
|
234
|
+
// Compare fixed segments rather than priority, since nesting under default routes raises the priority
|
|
235
|
+
return countFixedSegments(wildcard) > maxFullFixed ? wildcard : fullMatch;
|
|
236
|
+
};
|
|
237
|
+
/** Returns whether the chain matched the path segments only by ignoring some trailing segments. */
|
|
238
|
+
const isPartialMatch = (segments, chain) => {
|
|
239
|
+
const matched = chainToSegments(chain);
|
|
240
|
+
return matched !== null && matched.length < segments.filter((s) => s !== '').length;
|
|
241
|
+
};
|
|
242
|
+
const chainEndsWithWildcard = (chain) => {
|
|
243
|
+
return endsWithWildcard(chain[chain.length - 1].segments);
|
|
244
|
+
};
|
|
245
|
+
/** Returns whether the wildcard chain beats `best` by fixed segments, then by priority. */
|
|
246
|
+
const winsWildcard = (chain, best) => {
|
|
247
|
+
const chainFixed = countFixedSegments(chain);
|
|
248
|
+
const bestFixed = countFixedSegments(best);
|
|
249
|
+
return chainFixed > bestFixed || (chainFixed === bestFixed && computePriority(chain) > computePriority(best));
|
|
250
|
+
};
|
|
251
|
+
const countFixedSegments = (chain) => {
|
|
252
|
+
let count = 0;
|
|
253
|
+
for (const route of chain) {
|
|
254
|
+
for (const segment of withoutWildcard(route.segments)) {
|
|
255
|
+
if (segment !== '' && segment[0] !== ':') {
|
|
256
|
+
count++;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return count;
|
|
186
261
|
};
|
|
187
262
|
/**
|
|
188
263
|
* Computes the priority of a chain.
|
|
@@ -194,12 +269,14 @@ export const findChainForSegments = (segments, chains) => {
|
|
|
194
269
|
* - /path/to/page
|
|
195
270
|
*
|
|
196
271
|
* The second one will be given a higher priority because "page" is a fixed segment (vs ":where", a parameter segment).
|
|
272
|
+
*
|
|
273
|
+
* A trailing wildcard is not counted.
|
|
197
274
|
*/
|
|
198
275
|
export const computePriority = (chain) => {
|
|
199
276
|
let score = 1;
|
|
200
277
|
let level = 1;
|
|
201
278
|
for (const route of chain) {
|
|
202
|
-
for (const segment of route.segments) {
|
|
279
|
+
for (const segment of withoutWildcard(route.segments)) {
|
|
203
280
|
if (segment[0] === ':') {
|
|
204
281
|
score += Math.pow(1, level);
|
|
205
282
|
}
|
|
@@ -221,4 +298,10 @@ export class RouterSegments {
|
|
|
221
298
|
}
|
|
222
299
|
return '';
|
|
223
300
|
}
|
|
301
|
+
/** Consumes and returns the remaining non empty segments. */
|
|
302
|
+
rest() {
|
|
303
|
+
const rest = this.segments.filter((s) => s !== '');
|
|
304
|
+
this.segments = [];
|
|
305
|
+
return rest;
|
|
306
|
+
}
|
|
224
307
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { ROUTER_INTENT_FORWARD } from "./constants";
|
|
4
|
+
import { ROUTER_INTENT_FORWARD, WILDCARD_PARAM } from "./constants";
|
|
5
5
|
/** Join the non empty segments with "/". */
|
|
6
6
|
export const generatePath = (segments) => {
|
|
7
7
|
const path = segments.filter((s) => s.length > 0).join('/');
|
|
@@ -34,12 +34,13 @@ export const writeSegments = (history, root, useHash, segments, direction, state
|
|
|
34
34
|
*
|
|
35
35
|
* Notes:
|
|
36
36
|
* - parameter segments of the form :param are replaced with their value,
|
|
37
|
-
* - null is returned when a value is missing for any parameter segment
|
|
37
|
+
* - null is returned when a value is missing for any parameter segment,
|
|
38
|
+
* - a trailing wildcard is replaced with the path it matched.
|
|
38
39
|
*/
|
|
39
40
|
export const chainToSegments = (chain) => {
|
|
40
41
|
const segments = [];
|
|
41
42
|
for (const route of chain) {
|
|
42
|
-
for (const segment of route.segments) {
|
|
43
|
+
for (const segment of withoutWildcard(route.segments)) {
|
|
43
44
|
if (segment[0] === ':') {
|
|
44
45
|
const param = route.params && route.params[segment.slice(1)];
|
|
45
46
|
if (!param) {
|
|
@@ -51,9 +52,22 @@ export const chainToSegments = (chain) => {
|
|
|
51
52
|
segments.push(segment);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
segments.push(...wildcardSegments(route));
|
|
54
56
|
}
|
|
55
57
|
return segments;
|
|
56
58
|
};
|
|
59
|
+
/** Only a `*` in the last segment of a route url is a wildcard. Elsewhere it's matched literally. */
|
|
60
|
+
export const endsWithWildcard = (segments) => {
|
|
61
|
+
return segments[segments.length - 1] === '*';
|
|
62
|
+
};
|
|
63
|
+
/** Returns the path segments matched by the trailing wildcard of the route. */
|
|
64
|
+
const wildcardSegments = (route) => {
|
|
65
|
+
const pathMatch = endsWithWildcard(route.segments) ? route.params?.[WILDCARD_PARAM] : undefined;
|
|
66
|
+
return pathMatch ? pathMatch.split('/').filter((s) => s.length > 0) : [];
|
|
67
|
+
};
|
|
68
|
+
export const withoutWildcard = (segments) => {
|
|
69
|
+
return endsWithWildcard(segments) ? segments.slice(0, -1) : segments;
|
|
70
|
+
};
|
|
57
71
|
/**
|
|
58
72
|
* Removes the prefix segments from the path segments.
|
|
59
73
|
*
|
package/dist/docs.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-10-
|
|
2
|
+
"timestamp": "2026-10-30T18:14:29",
|
|
3
3
|
"compiler": {
|
|
4
4
|
"name": "@stencil/core",
|
|
5
5
|
"version": "4.45.2",
|
|
@@ -14038,6 +14038,32 @@
|
|
|
14038
14038
|
"getter": false,
|
|
14039
14039
|
"setter": false
|
|
14040
14040
|
},
|
|
14041
|
+
{
|
|
14042
|
+
"name": "errorText",
|
|
14043
|
+
"type": "string | undefined",
|
|
14044
|
+
"complexType": {
|
|
14045
|
+
"original": "string",
|
|
14046
|
+
"resolved": "string | undefined",
|
|
14047
|
+
"references": {}
|
|
14048
|
+
},
|
|
14049
|
+
"mutable": false,
|
|
14050
|
+
"attr": "error-text",
|
|
14051
|
+
"reflectToAttr": false,
|
|
14052
|
+
"docs": "Text that is placed under the input boxes and displayed when an error is detected.",
|
|
14053
|
+
"docsTags": [],
|
|
14054
|
+
"values": [
|
|
14055
|
+
{
|
|
14056
|
+
"type": "string"
|
|
14057
|
+
},
|
|
14058
|
+
{
|
|
14059
|
+
"type": "undefined"
|
|
14060
|
+
}
|
|
14061
|
+
],
|
|
14062
|
+
"optional": true,
|
|
14063
|
+
"required": false,
|
|
14064
|
+
"getter": false,
|
|
14065
|
+
"setter": false
|
|
14066
|
+
},
|
|
14041
14067
|
{
|
|
14042
14068
|
"name": "fill",
|
|
14043
14069
|
"type": "\"outline\" | \"solid\" | undefined",
|
|
@@ -14075,6 +14101,64 @@
|
|
|
14075
14101
|
"getter": false,
|
|
14076
14102
|
"setter": false
|
|
14077
14103
|
},
|
|
14104
|
+
{
|
|
14105
|
+
"name": "helperText",
|
|
14106
|
+
"type": "string | undefined",
|
|
14107
|
+
"complexType": {
|
|
14108
|
+
"original": "string",
|
|
14109
|
+
"resolved": "string | undefined",
|
|
14110
|
+
"references": {}
|
|
14111
|
+
},
|
|
14112
|
+
"mutable": false,
|
|
14113
|
+
"attr": "helper-text",
|
|
14114
|
+
"reflectToAttr": false,
|
|
14115
|
+
"docs": "Text that is placed under the input boxes and displayed when no error is detected.",
|
|
14116
|
+
"docsTags": [],
|
|
14117
|
+
"values": [
|
|
14118
|
+
{
|
|
14119
|
+
"type": "string"
|
|
14120
|
+
},
|
|
14121
|
+
{
|
|
14122
|
+
"type": "undefined"
|
|
14123
|
+
}
|
|
14124
|
+
],
|
|
14125
|
+
"optional": true,
|
|
14126
|
+
"required": false,
|
|
14127
|
+
"getter": false,
|
|
14128
|
+
"setter": false
|
|
14129
|
+
},
|
|
14130
|
+
{
|
|
14131
|
+
"name": "inputLabelFormatter",
|
|
14132
|
+
"type": "((index: number, length: number) => string) | undefined",
|
|
14133
|
+
"complexType": {
|
|
14134
|
+
"original": "InputOtpLabelFormatter",
|
|
14135
|
+
"resolved": "((index: number, length: number) => string) | undefined",
|
|
14136
|
+
"references": {
|
|
14137
|
+
"InputOtpLabelFormatter": {
|
|
14138
|
+
"location": "import",
|
|
14139
|
+
"path": "./input-otp-interface",
|
|
14140
|
+
"id": "src/components/input-otp/input-otp-interface.ts::InputOtpLabelFormatter",
|
|
14141
|
+
"referenceLocation": "InputOtpLabelFormatter"
|
|
14142
|
+
}
|
|
14143
|
+
}
|
|
14144
|
+
},
|
|
14145
|
+
"mutable": false,
|
|
14146
|
+
"reflectToAttr": false,
|
|
14147
|
+
"docs": "A callback used to format the accessible name of each input box. It\nreceives the 0-based `index` of the box and the total `length`. By default\nthe name is \"Input 1 of 4\", or \"PIN, 1 of 4\" with an `aria-label` of \"PIN\".\n\nSee https://ionicframework.com/docs/troubleshooting/runtime#accessing-this\nif you need to access `this` from within the callback.",
|
|
14148
|
+
"docsTags": [],
|
|
14149
|
+
"values": [
|
|
14150
|
+
{
|
|
14151
|
+
"type": "((index: number, length: number) => string)"
|
|
14152
|
+
},
|
|
14153
|
+
{
|
|
14154
|
+
"type": "undefined"
|
|
14155
|
+
}
|
|
14156
|
+
],
|
|
14157
|
+
"optional": true,
|
|
14158
|
+
"required": false,
|
|
14159
|
+
"getter": false,
|
|
14160
|
+
"setter": false
|
|
14161
|
+
},
|
|
14078
14162
|
{
|
|
14079
14163
|
"name": "inputmode",
|
|
14080
14164
|
"type": "\"decimal\" | \"email\" | \"none\" | \"numeric\" | \"search\" | \"tel\" | \"text\" | \"url\" | undefined",
|
|
@@ -27325,7 +27409,7 @@
|
|
|
27325
27409
|
"mutable": false,
|
|
27326
27410
|
"attr": "url",
|
|
27327
27411
|
"reflectToAttr": false,
|
|
27328
|
-
"docs": "Relative path that needs to match in order for this route to apply.\n\nAccepts paths similar to expressjs so that you can define parameters\nin the url /foo/:bar where bar would be available in incoming props.",
|
|
27412
|
+
"docs": "Relative path that needs to match in order for this route to apply.\n\nAccepts paths similar to expressjs so that you can define parameters\nin the url /foo/:bar where bar would be available in incoming props.\n\nA url ending in `*`, such as `*` or `/admin/*`, matches any remaining path,\nincluding none, so `/admin/*` also matches `/admin`. It only wins over a\nroute that matches the whole path when it has more fixed segments, so\n`/admin/*` wins over `/:section/:id` but not over `/admin/:id`. It always\nwins over a route that matches by ignoring trailing segments, such as\n`/admin` for `/admin/users`. The matched path is passed in the `pathMatch`\nprop without a leading `/`. A `*` on a route with child routes is ignored.",
|
|
27329
27413
|
"docsTags": [
|
|
27330
27414
|
{
|
|
27331
27415
|
"name": "default",
|
|
@@ -27389,7 +27473,7 @@
|
|
|
27389
27473
|
"mutable": false,
|
|
27390
27474
|
"attr": "from",
|
|
27391
27475
|
"reflectToAttr": false,
|
|
27392
|
-
"docs": "A redirect route, redirects \"from\" a URL \"to\" another URL. This property is that \"from\" URL.\nIt needs to be an exact match of the navigated URL in order to apply
|
|
27476
|
+
"docs": "A redirect route, redirects \"from\" a URL \"to\" another URL. This property is that \"from\" URL.\nIt needs to be an exact match of the navigated URL in order to apply, unless it contains a `*`.\nA `*` matches the rest of the path from that segment on, as long as the path has at least as\nmany segments as `from`, so `/admin/*` matches `/admin/posts` but not `/admin`.\n\nThe path specified in this value is always an absolute path, even if the initial `/` slash\nis not specified.",
|
|
27393
27477
|
"docsTags": [],
|
|
27394
27478
|
"values": [
|
|
27395
27479
|
{
|
|
@@ -37770,6 +37854,11 @@
|
|
|
37770
37854
|
"docstring": "Values are converted to strings when emitted which is\nwhy we do not have a `number` type here even though the\n`value` prop accepts a `number` type.",
|
|
37771
37855
|
"path": "src/components/input/input-interface.ts"
|
|
37772
37856
|
},
|
|
37857
|
+
"src/components/input-otp/input-otp-interface.ts::InputOtpLabelFormatter": {
|
|
37858
|
+
"declaration": "(index: number, length: number) => string",
|
|
37859
|
+
"docstring": "",
|
|
37860
|
+
"path": "src/components/input-otp/input-otp-interface.ts"
|
|
37861
|
+
},
|
|
37773
37862
|
"src/components/input-otp/input-otp-interface.ts::InputOtpInputEventDetail": {
|
|
37774
37863
|
"declaration": "export interface InputOtpInputEventDetail {\n value?: string | null;\n event?: Event;\n}",
|
|
37775
37864
|
"docstring": "Values are converted to strings when emitted which is\nwhy we do not have a `number` type here even though the\n`value` prop accepts a `number` type.",
|