@ramstack/alpinegear-router 1.1.0-preview.5 → 1.1.0
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/README.md +24 -3
- package/alpinegear-router.esm.js +29 -32
- package/alpinegear-router.esm.min.js +1 -1
- package/alpinegear-router.js +29 -32
- package/alpinegear-router.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
@@ -56,7 +56,7 @@ Alpine.start();
|
|
56
56
|
|
57
57
|
<nav>
|
58
58
|
<a x-router:link href="/">Home</a>
|
59
|
-
<a x-router:link href="/about">About</a>
|
59
|
+
<a x-router:link.replace href="/about">About</a>
|
60
60
|
</nav>
|
61
61
|
|
62
62
|
<!-- Render the matching route -->
|
@@ -172,6 +172,27 @@ In this case:
|
|
172
172
|
* `x-router:link` will locate the nested `<a>` element within the parent and use its `href` for routing.
|
173
173
|
* The `$active` state will correctly reflect whether the nested link's `href` matches the current route.
|
174
174
|
|
175
|
+
### Modifier `replace`
|
176
|
+
|
177
|
+
The `replace` modifier changes the default navigation behavior of the router. When a link with this modifier is clicked,
|
178
|
+
it triggers `$router.navigate(href, /* replace */ true)`.
|
179
|
+
|
180
|
+
Unlike the default behavior, which adds a new entry to the browser's history stack, this option replaces
|
181
|
+
the current history entry with the new URL. As a result, the user's navigation history remains unchanged,
|
182
|
+
and pressing the **"Back"** button will skip over the replaced entry.
|
183
|
+
|
184
|
+
```html
|
185
|
+
<div x-router:hash>
|
186
|
+
...
|
187
|
+
<nav>
|
188
|
+
<a x-router:link href="/">Home</a>
|
189
|
+
<!-- The "replace" modifier ensures that clicking this link
|
190
|
+
replaces the current history entry instead of adding a new one -->
|
191
|
+
<a x-router:link.replace href="/about">About</a>
|
192
|
+
</nav>
|
193
|
+
</div>
|
194
|
+
```
|
195
|
+
|
175
196
|
## Inline and External templates
|
176
197
|
Routes can be defined using either **inline templates** or **external templates**:
|
177
198
|
|
@@ -212,8 +233,8 @@ Returns `true` or `false`, indicating whether a `x-router:link` corresponds to t
|
|
212
233
|
|
213
234
|
```html
|
214
235
|
<nav>
|
215
|
-
<a x-router:link href="/" class="{
|
216
|
-
<a x-router:link href="/about" class="{
|
236
|
+
<a x-router:link href="/" class="{ active: $active }">Home</a>
|
237
|
+
<a x-router:link href="/about" class="{ active: $active }">About</a>
|
217
238
|
</nav>
|
218
239
|
```
|
219
240
|
|
package/alpinegear-router.esm.js
CHANGED
@@ -4,6 +4,7 @@ const is_nullish = value => value === null || value === undefined;
|
|
4
4
|
const is_template = el => el instanceof HTMLTemplateElement;
|
5
5
|
const is_function = value => typeof value === "function";
|
6
6
|
const as_array = value => is_array(value) ? value : [value];
|
7
|
+
const has_modifier = (modifiers, modifier) => modifiers.includes(modifier);
|
7
8
|
|
8
9
|
function assert(value, message) {
|
9
10
|
if (!value) {
|
@@ -468,16 +469,19 @@ async function load_template(path) {
|
|
468
469
|
return fragment;
|
469
470
|
}
|
470
471
|
|
471
|
-
function route({ directive,
|
472
|
+
function route({ directive, $data }) {
|
472
473
|
directive("route", (el, { expression, value, modifiers }, { cleanup, evaluate }) => {
|
473
474
|
if (!is_template(el)) {
|
474
475
|
warn("x-route can only be used on a 'template' tag");
|
475
476
|
return;
|
476
477
|
}
|
477
478
|
|
478
|
-
|
479
|
+
|
480
|
+
|
481
|
+
|
482
|
+
const route = el._r_route;
|
479
483
|
|
480
|
-
if (
|
484
|
+
if (!route && (value === "view" || value === "handler")) {
|
481
485
|
warn(`no x-route directive found`);
|
482
486
|
return;
|
483
487
|
}
|
@@ -497,20 +501,18 @@ function route({ directive, magic, $data }) {
|
|
497
501
|
}
|
498
502
|
|
499
503
|
function process_route() {
|
500
|
-
const router =
|
501
|
-
if (
|
502
|
-
|
503
|
-
return;
|
504
|
-
}
|
504
|
+
const router = $data(el)?.$router;
|
505
|
+
if (router) {
|
506
|
+
const view = () => new Promise(resolve => resolve(el.content));
|
505
507
|
|
506
|
-
|
508
|
+
el._r_route = Object.assign(new RoutePattern(expression), { el, view, handler: () => Promise.resolve() });
|
509
|
+
router.routes.push(el._r_route);
|
507
510
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
});
|
511
|
+
cleanup(() => router.routes = router.routes.filter(r => r !== el._r_route));
|
512
|
+
}
|
513
|
+
else {
|
514
|
+
warn(`no x-router directive found`);
|
515
|
+
}
|
514
516
|
}
|
515
517
|
|
516
518
|
function process_handler() {
|
@@ -540,8 +542,6 @@ function route({ directive, magic, $data }) {
|
|
540
542
|
});
|
541
543
|
}
|
542
544
|
});
|
543
|
-
|
544
|
-
magic("route", el => closest(el, n => n._r_router)?._r_router.values);
|
545
545
|
}
|
546
546
|
|
547
547
|
let data;
|
@@ -674,13 +674,13 @@ function watch(get_value, callback, options = null) {
|
|
674
674
|
return () => release(handle);
|
675
675
|
}
|
676
676
|
|
677
|
-
function router({ directive, magic, reactive }) {
|
678
|
-
directive("router", (el, {
|
677
|
+
function router({ $data, addScopeToNode, directive, magic, reactive }) {
|
678
|
+
directive("router", (el, { modifiers, value }, { cleanup }) => {
|
679
679
|
value || (value = "html5");
|
680
680
|
|
681
|
-
const router =
|
681
|
+
const router = $data(el).$router;
|
682
682
|
|
683
|
-
if (
|
683
|
+
if (!router && (value === "outlet" || value === "link")) {
|
684
684
|
warn(`no x-router directive found`);
|
685
685
|
return;
|
686
686
|
}
|
@@ -708,12 +708,10 @@ function router({ directive, magic, reactive }) {
|
|
708
708
|
const values = reactive({
|
709
709
|
pattern: "",
|
710
710
|
path: "",
|
711
|
-
params:
|
711
|
+
params: {}
|
712
712
|
});
|
713
713
|
|
714
|
-
const api =
|
715
|
-
? evaluate(expression)
|
716
|
-
: create_history(value);
|
714
|
+
const api = create_history(value);
|
717
715
|
|
718
716
|
const router = {
|
719
717
|
routes: [],
|
@@ -738,7 +736,7 @@ function router({ directive, magic, reactive }) {
|
|
738
736
|
}
|
739
737
|
};
|
740
738
|
|
741
|
-
el
|
739
|
+
addScopeToNode(el, { $route: values, $router: router });
|
742
740
|
|
743
741
|
function activate(route, path, params) {
|
744
742
|
if (route.nodes?.length && values.path === path) {
|
@@ -749,7 +747,7 @@ function router({ directive, magic, reactive }) {
|
|
749
747
|
|
750
748
|
values.path = path;
|
751
749
|
values.pattern = route.template;
|
752
|
-
values.params = params;
|
750
|
+
values.params = params ?? {};
|
753
751
|
|
754
752
|
router.active = route;
|
755
753
|
|
@@ -775,6 +773,7 @@ function router({ directive, magic, reactive }) {
|
|
775
773
|
for (let n of router.active.nodes ?? []) {
|
776
774
|
n.remove();
|
777
775
|
}
|
776
|
+
|
778
777
|
router.active.nodes = null;
|
779
778
|
router.active = null;
|
780
779
|
}
|
@@ -816,7 +815,7 @@ function router({ directive, magic, reactive }) {
|
|
816
815
|
|
817
816
|
e.preventDefault();
|
818
817
|
|
819
|
-
router.navigate(`${ link.pathname }${ link.search }${ link.hash }
|
818
|
+
router.navigate(`${ link.pathname }${ link.search }${ link.hash }`, has_modifier(modifiers, "replace"));
|
820
819
|
});
|
821
820
|
|
822
821
|
cleanup(unsubscribe);
|
@@ -839,11 +838,9 @@ function router({ directive, magic, reactive }) {
|
|
839
838
|
}
|
840
839
|
});
|
841
840
|
|
842
|
-
magic("router", el => closest(el, n => n._r_router)?._r_router);
|
843
|
-
|
844
841
|
magic("active", el => {
|
845
|
-
const router =
|
846
|
-
if (
|
842
|
+
const router = $data(el).$router;
|
843
|
+
if (!router) {
|
847
844
|
warn("No x-router directive found");
|
848
845
|
return false;
|
849
846
|
}
|
@@ -1 +1 @@
|
|
1
|
-
const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=
|
1
|
+
const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=Object.freeze({regex(e){const t=new RegExp(e);return{test:e=>t.test(e)}},bool(){return{test:e=>/^(?:true|false)$/i.test(e),transform:e=>4===e.length}},int(){return{test:e=>/^-?\d+$/.test(e),transform:e=>+e}},number(){return{test:e=>/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(e)&&isFinite(parseFloat(e)),transform:e=>parseFloat(e)}},alpha(){return{test:e=>/^[a-z]+$/i.test(e)}},min(e){return{test:t=>t>=+e}},max(e){return{test:t=>+e>=t}},range(e){let[t,n]=e.split(",",2).map((e=>e.trim()));return{test:e=>e>=+t&&+n>=e}},length(e){let t=e.split(",").map((e=>e.trim()));return{test:2==t.length?e=>e.length>=+t[0]&&+t[1]>=e.length:e=>e.length===+t[0]}},minlength(e){return{test:t=>t.length>=+e}},maxlength(e){return{test:t=>+e>=t.length}}});class u{#e;#t;#n;#r;#a;get template(){return this.#t}get regex(){return this.#e}get constraints(){return this.#a}constructor(e,t=null){this.#t=e,this.#e=function(e,t,r,a){t.push(...function(e,t){return function(e){e.find((e=>e.parts.length>1&&e.parts.every((e=>e.optional))))&&u("Using all segment parameters as optional is not permitted");const r=new Map;return e.flatMap((e=>e.parts)).forEach(((e,a,o)=>{if("literal"!==e.kind||0>e.value.indexOf("?")||u("Literal segments cannot contain the '?' character"),"parameter"===e.kind){e.catch_all&&a!==o.length-1&&u("A catch-all parameter can only appear as the last segment"),r.has(e.name)&&u(`The route parameter name '${e.name}' appears more than one time`),"*"===e.quantifier&&n(e.default)&&(e.default=""),""===e.default&&"*"!==e.quantifier&&(e.default=null),r.set(e.name,!0);for(let r of e.constraints){const e=t?.[r.name]??s[r.name];n(e)&&u(`Unknown constraint '${r.name}'`),Object.assign(r,e(r.argument))}}})),e}(function(){const t=[];for(let n=0;e.length>n;){const e=r(n);e.template&&t.push(e),n+=e.template.length+1}return t}());function r(t){let n=[],r=t;for(;e.length>r&&"/"!==e[r];){const e=o(r)||a(r);n.push(e),r+=e.template.length}return{template:e.slice(t,r),parts:n}}function a(t){if("{"!==e[t])return null;const r=i(t),a=e.slice(t,t+r.length+2),o=function(e){const t=e.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;return n(t)&&u("Invalid parameter name"),t}(r),s=/[*+?]/.exec(r[o.length])?.[0]??"",c=function(e,t){const r=[];for(let a=t;e.length>a;){":"!==e[a]&&u();const t=l(e.slice(a+1));a+=t.length+1;const o="("===e[a]?i(a,e):null;n(o)||(a+=o.length+2),t||o||u(),r.push({name:"="===t?"default":t||"regex",argument:o??""})}return r}(r,o.length+s.length);return{name:o,kind:"parameter",template:a,quantifier:s,constraints:c.filter((e=>"default"!==e.name)),default:c.find((e=>"default"===e.name))?.argument,required:"+"===s||""===s,optional:"?"===s||"*"===s,catch_all:"+"===s||"*"===s}}function o(t){for(let n=t;;n++)if(n>=e.length||"/"===e[n]||"{"===e[n]){if(n===t)return null;const r=e.slice(t,n);return{kind:"literal",template:r,value:r}}}function i(t,n){n??=e;const r=[];e:for(let e=t;n.length>e;e++){switch(n[e]){case"{":r.push("}");break;case"(":r.push(")");break;case"}":case")":if(r.pop()!==n[e])break e}if(0===r.length)return n.slice(t+1,e)}u()}function l(e){const t=e.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;return n(t)&&u("Invalid constraint name"),t}function u(t="Invalid pattern"){throw new Error(`${t}: ${e}`)}}(e,a));let o=t.map((e=>e.parts.map(((t,n)=>{if("literal"===t.kind)return n?t.value:`/${t.value}`;if(r.set(t.name,t),1===e.parts.length&&"?"===t.quantifier)return`(?:/(?<${t.name}>[^/]+?))?`;if(t.catch_all){let e=`(?<${t.name}>.${t.quantifier})`;return n||(e=`(?:/${e})`),"*"===t.quantifier&&(e+="?"),"*"===t.quantifier?e+"?":e}{const e=`(?<${t.name}>[^/]+?)${t.quantifier}`;return n?e:`/${e}`}})).join(""))).join("")||"/";return"/"!==o&&(o+="/?"),new RegExp(`^${o}$`)}(e,this.#n=[],this.#r=new Map,this.#a=t??{})}match(e){let t=this.#e.exec(e);if(n(t))return null;t=t.groups??{};for(let[e,r]of this.#r.entries()){let a=t[e];if(n(a)&&n(r.default))continue;a||n(r.default)||(a=r.default);const o=r.catch_all?a.split("/").filter((e=>e.length)):[a];for(let e=0;o.length>e;e++)for(let t of r.constraints){if(t.test&&!t.test(o[e]))return null;t.transform&&(o[e]=t.transform(o[e]))}t[e]=r.catch_all?o:o[0]}return t}resolve(e){e=new Map(Object.entries(e));const t=[];for(let r of this.#n){const a=[];for(let t of r.parts)if("literal"===t.kind)a.push(t.value);else{let r=e.get(t.name);if(e.delete(t.name),(n(r)||""===r)&&(r=this.#r.get(t.name)?.default,t.catch_all&&r&&(r=r.split("/"))),n(r)||""===r){if(t.required)return null;if(t.optional&&t.default===r)continue}t.catch_all?(r=o(r),a.push(...r.map((e=>encodeURIComponent(e))).join("/"))):a.push(encodeURIComponent(r))}a.length&&t.push(a.join(""))}let r=[...e.entries()].map((([e,t])=>encodeURIComponent(e)+"="+encodeURIComponent(t))).join("&");r&&(r="?"+r);const a=t.join("/")+r;return"/"!==a[0]?"/"+a:a}}function c({directive:t,$data:a}){t("route",((t,{expression:o,value:l},{cleanup:s,evaluate:c})=>{if(!r(t))return void e("x-route can only be used on a 'template' tag");const f=t._r_route;if(f||"view"!==l&&"handler"!==l)switch(l){case"view":f.view=()=>async function(t){let n;try{n=await fetch(t)}catch{}if(!n?.ok)return e(`Failed to load template from ${t}`),new DocumentFragment;const r=new DocumentFragment,a=(new DOMParser).parseFromString(await n.text(),"text/html");return r.append(...a.body.childNodes),r}(o),s((()=>{f.view=()=>new Promise((e=>e(new DocumentFragment)))}));break;case"handler":!function(){o||(o="[]"),o.startsWith("[")||(o=`[${o}]`);const e=c(o).map(i),r=a(t);f.handler=async t=>{for(let a of e){const e=await a.call(r,t);if(!n(e))return e}},s((()=>f.handler=null))}();break;default:!function(){const n=a(t)?.$router;if(n){const e=()=>new Promise((e=>e(t.content)));t._r_route=Object.assign(new u(o),{el:t,view:e,handler:()=>Promise.resolve()}),n.routes.push(t._r_route),s((()=>n.routes=n.routes.filter((e=>e!==t._r_route))))}else e("no x-router directive found")}()}else e("no x-route directive found")}))}let f,h;function p(){for(let e in f)e in location&&(f[e]=location[e])}const m={get path(){return h.hash.slice(1)||"/"},get location(){return h},resolve(e){let t=new URL(e);return t.hash?t.hash.slice(1)||"/":t.pathname},navigate(e,t=!1){0>e.indexOf("#")&&(e="#"+e),g(e,t)}},d={get path(){return h.pathname},get location(){return h},resolve(e){return new URL(e).pathname},navigate(e,t=!1){g(e,t)}};function g(e,t){history[t?"replaceState":"pushState"]({},"",e),h.refresh()}const v={html5:d,hash:m};function w({$data:t,addScopeToNode:n,directive:a,magic:o,reactive:i}){a("router",((a,{modifiers:o,value:s},{cleanup:u})=>{s||(s="html5");const c=t(a).$router;if(c||"outlet"!==s&&"link"!==s)switch(s){case"outlet":c.outlet?e("x-router:outlet already specified",c.outlet,a):(c.outlet=a,u((()=>c.outlet=null)));break;case"link":!function(){let t=function(t){if($(t))return t;const n=t.querySelectorAll("a");return 1!==n.length&&e(`Expected exactly one link, but found ${n.length}`),n[0]}(a);if(t){a._r_routerlink=t;const e=(t.getAttribute("target")??"").indexOf("_blank")>=0,n=l(t,"click",(n=>{n.metaKey||n.altKey||n.ctrlKey||n.shiftKey||n.defaultPrevented||n.button>0||e||(n.preventDefault(),c.navigate(`${t.pathname}${t.search}${t.hash}`,(e=>e.includes("replace"))(o)))}));u(n)}}();break;default:!function(){if(r(a))return void e("x-router cannot be used on a 'template' tag");const t=i({pattern:"",path:"",params:{}}),o=function(t){h??=(f||(f=Alpine.reactive({hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",port:0,protocol:"",search:"",refresh(){p()}}),p(),l(window,"hashchange",p),l(window,"popstate",p)),f),t||="html5";let n=v[t];return n||(e(`Unknown history API: ${t}`),n=d),n}(s),c={routes:[],outlet:null,active:null,history:o,values:t,async match(e){for(let t of this.routes){const n=t.match(e);if(n){const r={router:c,route:t,params:n,path:e};if(!1!==await t.handler(r))return r}}},navigate(e,t=!1){return o.navigate(e,t),!0}};function m(){if(c.active){for(let e of c.active.nodes??[])e.remove();c.active.nodes=null,c.active=null}}n(a,{$route:t,$router:c});const g=function(e,t,n=null){const{effect:r,release:a}=Alpine;let o,i,l=!1;const s=r((()=>{o=e(),l||(n?.deep&&JSON.stringify(o),i=o),(l||(n?.immediate??1))&&setTimeout((()=>{t(o,i),i=o}),0),l=!0}));return()=>a(s)}((()=>o.path),(async e=>{const n=await c.match(e);n?e===o.path&&function(e,n,a){if(e.nodes?.length&&t.path===n)return;m(),t.path=n,t.pattern=e.template,t.params=a??{},c.active=e;const o=c.outlet;o&&e.view().then((i=>{t.path===n&&t.pattern===e.template&&JSON.stringify(t.params)===JSON.stringify(a)&&(e.nodes=[...i.cloneNode(!0).childNodes],r(o)?e.nodes.forEach((e=>o.parentElement.insertBefore(e,o))):e.nodes.forEach((e=>o.append(e))))}))}(n.route,n.path,n.params):m()}));u(g),u(m)}()}else e("no x-router directive found")})),o("active",(n=>{const r=t(n).$router;if(!r)return e("No x-router directive found"),!1;JSON.stringify(r.values);const a=$(n)?n:(e=>{for(;e&&!e._r_routerlink;)e=(e._x_teleportBack??e).parentElement;return e})(n)?._r_routerlink;return a?r.history.resolve(a.href)===r.values.path:(n._r_routerlink_init?e("x-router:link directive not found",n):queueMicrotask((()=>{n._r_routerlink_init=!0,r.values.path=r.values.path})),!1)}))}function $(e){return"A"===e.tagName.toUpperCase()}function _(e){window.RoutePattern=u,e.plugin([w,c])}export{u as RoutePattern,_ as router};
|
package/alpinegear-router.js
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
const is_template = el => el instanceof HTMLTemplateElement;
|
8
8
|
const is_function = value => typeof value === "function";
|
9
9
|
const as_array = value => is_array(value) ? value : [value];
|
10
|
+
const has_modifier = (modifiers, modifier) => modifiers.includes(modifier);
|
10
11
|
|
11
12
|
function assert(value, message) {
|
12
13
|
if (!value) {
|
@@ -471,16 +472,19 @@
|
|
471
472
|
return fragment;
|
472
473
|
}
|
473
474
|
|
474
|
-
function route({ directive,
|
475
|
+
function route({ directive, $data }) {
|
475
476
|
directive("route", (el, { expression, value, modifiers }, { cleanup, evaluate }) => {
|
476
477
|
if (!is_template(el)) {
|
477
478
|
warn("x-route can only be used on a 'template' tag");
|
478
479
|
return;
|
479
480
|
}
|
480
481
|
|
481
|
-
|
482
|
+
|
483
|
+
|
484
|
+
|
485
|
+
const route = el._r_route;
|
482
486
|
|
483
|
-
if (
|
487
|
+
if (!route && (value === "view" || value === "handler")) {
|
484
488
|
warn(`no x-route directive found`);
|
485
489
|
return;
|
486
490
|
}
|
@@ -500,20 +504,18 @@
|
|
500
504
|
}
|
501
505
|
|
502
506
|
function process_route() {
|
503
|
-
const router =
|
504
|
-
if (
|
505
|
-
|
506
|
-
return;
|
507
|
-
}
|
507
|
+
const router = $data(el)?.$router;
|
508
|
+
if (router) {
|
509
|
+
const view = () => new Promise(resolve => resolve(el.content));
|
508
510
|
|
509
|
-
|
511
|
+
el._r_route = Object.assign(new RoutePattern(expression), { el, view, handler: () => Promise.resolve() });
|
512
|
+
router.routes.push(el._r_route);
|
510
513
|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
});
|
514
|
+
cleanup(() => router.routes = router.routes.filter(r => r !== el._r_route));
|
515
|
+
}
|
516
|
+
else {
|
517
|
+
warn(`no x-router directive found`);
|
518
|
+
}
|
517
519
|
}
|
518
520
|
|
519
521
|
function process_handler() {
|
@@ -543,8 +545,6 @@
|
|
543
545
|
});
|
544
546
|
}
|
545
547
|
});
|
546
|
-
|
547
|
-
magic("route", el => closest(el, n => n._r_router)?._r_router.values);
|
548
548
|
}
|
549
549
|
|
550
550
|
let data;
|
@@ -677,13 +677,13 @@
|
|
677
677
|
return () => release(handle);
|
678
678
|
}
|
679
679
|
|
680
|
-
function router({ directive, magic, reactive }) {
|
681
|
-
directive("router", (el, {
|
680
|
+
function router({ $data, addScopeToNode, directive, magic, reactive }) {
|
681
|
+
directive("router", (el, { modifiers, value }, { cleanup }) => {
|
682
682
|
value || (value = "html5");
|
683
683
|
|
684
|
-
const router =
|
684
|
+
const router = $data(el).$router;
|
685
685
|
|
686
|
-
if (
|
686
|
+
if (!router && (value === "outlet" || value === "link")) {
|
687
687
|
warn(`no x-router directive found`);
|
688
688
|
return;
|
689
689
|
}
|
@@ -711,12 +711,10 @@
|
|
711
711
|
const values = reactive({
|
712
712
|
pattern: "",
|
713
713
|
path: "",
|
714
|
-
params:
|
714
|
+
params: {}
|
715
715
|
});
|
716
716
|
|
717
|
-
const api =
|
718
|
-
? evaluate(expression)
|
719
|
-
: create_history(value);
|
717
|
+
const api = create_history(value);
|
720
718
|
|
721
719
|
const router = {
|
722
720
|
routes: [],
|
@@ -741,7 +739,7 @@
|
|
741
739
|
}
|
742
740
|
};
|
743
741
|
|
744
|
-
el
|
742
|
+
addScopeToNode(el, { $route: values, $router: router });
|
745
743
|
|
746
744
|
function activate(route, path, params) {
|
747
745
|
if (route.nodes?.length && values.path === path) {
|
@@ -752,7 +750,7 @@
|
|
752
750
|
|
753
751
|
values.path = path;
|
754
752
|
values.pattern = route.template;
|
755
|
-
values.params = params;
|
753
|
+
values.params = params ?? {};
|
756
754
|
|
757
755
|
router.active = route;
|
758
756
|
|
@@ -778,6 +776,7 @@
|
|
778
776
|
for (let n of router.active.nodes ?? []) {
|
779
777
|
n.remove();
|
780
778
|
}
|
779
|
+
|
781
780
|
router.active.nodes = null;
|
782
781
|
router.active = null;
|
783
782
|
}
|
@@ -819,7 +818,7 @@
|
|
819
818
|
|
820
819
|
e.preventDefault();
|
821
820
|
|
822
|
-
router.navigate(`${ link.pathname }${ link.search }${ link.hash }
|
821
|
+
router.navigate(`${ link.pathname }${ link.search }${ link.hash }`, has_modifier(modifiers, "replace"));
|
823
822
|
});
|
824
823
|
|
825
824
|
cleanup(unsubscribe);
|
@@ -842,11 +841,9 @@
|
|
842
841
|
}
|
843
842
|
});
|
844
843
|
|
845
|
-
magic("router", el => closest(el, n => n._r_router)?._r_router);
|
846
|
-
|
847
844
|
magic("active", el => {
|
848
|
-
const router =
|
849
|
-
if (
|
845
|
+
const router = $data(el).$router;
|
846
|
+
if (!router) {
|
850
847
|
warn("No x-router directive found");
|
851
848
|
return false;
|
852
849
|
}
|
package/alpinegear-router.min.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
!function(){"use strict";const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=
|
1
|
+
!function(){"use strict";const e=(...e)=>console.warn("alpine-gear.js:",...e),t=Array.isArray,n=e=>null==e,r=e=>e instanceof HTMLTemplateElement,a=e=>"function"==typeof e,o=e=>t(e)?e:[e],i=e=>a(e)&&"AsyncFunction"===e.constructor?.name?e:function(...t){const n=e.apply(this,t);return a(n?.then)?n:Promise.resolve(n)},l=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),s=Object.freeze({regex(e){const t=new RegExp(e);return{test:e=>t.test(e)}},bool(){return{test:e=>/^(?:true|false)$/i.test(e),transform:e=>4===e.length}},int(){return{test:e=>/^-?\d+$/.test(e),transform:e=>+e}},number(){return{test:e=>/^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/.test(e)&&isFinite(parseFloat(e)),transform:e=>parseFloat(e)}},alpha(){return{test:e=>/^[a-z]+$/i.test(e)}},min(e){return{test:t=>t>=+e}},max(e){return{test:t=>+e>=t}},range(e){let[t,n]=e.split(",",2).map((e=>e.trim()));return{test:e=>e>=+t&&+n>=e}},length(e){let t=e.split(",").map((e=>e.trim()));return{test:2==t.length?e=>e.length>=+t[0]&&+t[1]>=e.length:e=>e.length===+t[0]}},minlength(e){return{test:t=>t.length>=+e}},maxlength(e){return{test:t=>+e>=t.length}}});class u{#e;#t;#n;#r;#a;get template(){return this.#t}get regex(){return this.#e}get constraints(){return this.#a}constructor(e,t=null){this.#t=e,this.#e=function(e,t,r,a){t.push(...function(e,t){return function(e){e.find((e=>e.parts.length>1&&e.parts.every((e=>e.optional))))&&u("Using all segment parameters as optional is not permitted");const r=new Map;return e.flatMap((e=>e.parts)).forEach(((e,a,o)=>{if("literal"!==e.kind||0>e.value.indexOf("?")||u("Literal segments cannot contain the '?' character"),"parameter"===e.kind){e.catch_all&&a!==o.length-1&&u("A catch-all parameter can only appear as the last segment"),r.has(e.name)&&u(`The route parameter name '${e.name}' appears more than one time`),"*"===e.quantifier&&n(e.default)&&(e.default=""),""===e.default&&"*"!==e.quantifier&&(e.default=null),r.set(e.name,!0);for(let r of e.constraints){const e=t?.[r.name]??s[r.name];n(e)&&u(`Unknown constraint '${r.name}'`),Object.assign(r,e(r.argument))}}})),e}(function(){const t=[];for(let n=0;e.length>n;){const e=r(n);e.template&&t.push(e),n+=e.template.length+1}return t}());function r(t){let n=[],r=t;for(;e.length>r&&"/"!==e[r];){const e=o(r)||a(r);n.push(e),r+=e.template.length}return{template:e.slice(t,r),parts:n}}function a(t){if("{"!==e[t])return null;const r=i(t),a=e.slice(t,t+r.length+2),o=function(e){const t=e.match(/^(?<name>[a-z_$][a-z0-9_$-]*?)(?:[:?+*]|$)/i)?.groups?.name;return n(t)&&u("Invalid parameter name"),t}(r),s=/[*+?]/.exec(r[o.length])?.[0]??"",c=function(e,t){const r=[];for(let a=t;e.length>a;){":"!==e[a]&&u();const t=l(e.slice(a+1));a+=t.length+1;const o="("===e[a]?i(a,e):null;n(o)||(a+=o.length+2),t||o||u(),r.push({name:"="===t?"default":t||"regex",argument:o??""})}return r}(r,o.length+s.length);return{name:o,kind:"parameter",template:a,quantifier:s,constraints:c.filter((e=>"default"!==e.name)),default:c.find((e=>"default"===e.name))?.argument,required:"+"===s||""===s,optional:"?"===s||"*"===s,catch_all:"+"===s||"*"===s}}function o(t){for(let n=t;;n++)if(n>=e.length||"/"===e[n]||"{"===e[n]){if(n===t)return null;const r=e.slice(t,n);return{kind:"literal",template:r,value:r}}}function i(t,n){n??=e;const r=[];e:for(let e=t;n.length>e;e++){switch(n[e]){case"{":r.push("}");break;case"(":r.push(")");break;case"}":case")":if(r.pop()!==n[e])break e}if(0===r.length)return n.slice(t+1,e)}u()}function l(e){const t=e.match(/^(?<name>=|[a-z0-9_$]*)(?=[/:(]|$)/i)?.groups?.name;return n(t)&&u("Invalid constraint name"),t}function u(t="Invalid pattern"){throw new Error(`${t}: ${e}`)}}(e,a));let o=t.map((e=>e.parts.map(((t,n)=>{if("literal"===t.kind)return n?t.value:`/${t.value}`;if(r.set(t.name,t),1===e.parts.length&&"?"===t.quantifier)return`(?:/(?<${t.name}>[^/]+?))?`;if(t.catch_all){let e=`(?<${t.name}>.${t.quantifier})`;return n||(e=`(?:/${e})`),"*"===t.quantifier&&(e+="?"),"*"===t.quantifier?e+"?":e}{const e=`(?<${t.name}>[^/]+?)${t.quantifier}`;return n?e:`/${e}`}})).join(""))).join("")||"/";return"/"!==o&&(o+="/?"),new RegExp(`^${o}$`)}(e,this.#n=[],this.#r=new Map,this.#a=t??{})}match(e){let t=this.#e.exec(e);if(n(t))return null;t=t.groups??{};for(let[e,r]of this.#r.entries()){let a=t[e];if(n(a)&&n(r.default))continue;a||n(r.default)||(a=r.default);const o=r.catch_all?a.split("/").filter((e=>e.length)):[a];for(let e=0;o.length>e;e++)for(let t of r.constraints){if(t.test&&!t.test(o[e]))return null;t.transform&&(o[e]=t.transform(o[e]))}t[e]=r.catch_all?o:o[0]}return t}resolve(e){e=new Map(Object.entries(e));const t=[];for(let r of this.#n){const a=[];for(let t of r.parts)if("literal"===t.kind)a.push(t.value);else{let r=e.get(t.name);if(e.delete(t.name),(n(r)||""===r)&&(r=this.#r.get(t.name)?.default,t.catch_all&&r&&(r=r.split("/"))),n(r)||""===r){if(t.required)return null;if(t.optional&&t.default===r)continue}t.catch_all?(r=o(r),a.push(...r.map((e=>encodeURIComponent(e))).join("/"))):a.push(encodeURIComponent(r))}a.length&&t.push(a.join(""))}let r=[...e.entries()].map((([e,t])=>encodeURIComponent(e)+"="+encodeURIComponent(t))).join("&");r&&(r="?"+r);const a=t.join("/")+r;return"/"!==a[0]?"/"+a:a}}function c({directive:t,$data:a}){t("route",((t,{expression:o,value:l},{cleanup:s,evaluate:c})=>{if(!r(t))return void e("x-route can only be used on a 'template' tag");const f=t._r_route;if(f||"view"!==l&&"handler"!==l)switch(l){case"view":f.view=()=>async function(t){let n;try{n=await fetch(t)}catch{}if(!n?.ok)return e(`Failed to load template from ${t}`),new DocumentFragment;const r=new DocumentFragment,a=(new DOMParser).parseFromString(await n.text(),"text/html");return r.append(...a.body.childNodes),r}(o),s((()=>{f.view=()=>new Promise((e=>e(new DocumentFragment)))}));break;case"handler":!function(){o||(o="[]"),o.startsWith("[")||(o=`[${o}]`);const e=c(o).map(i),r=a(t);f.handler=async t=>{for(let a of e){const e=await a.call(r,t);if(!n(e))return e}},s((()=>f.handler=null))}();break;default:!function(){const n=a(t)?.$router;if(n){const e=()=>new Promise((e=>e(t.content)));t._r_route=Object.assign(new u(o),{el:t,view:e,handler:()=>Promise.resolve()}),n.routes.push(t._r_route),s((()=>n.routes=n.routes.filter((e=>e!==t._r_route))))}else e("no x-router directive found")}()}else e("no x-route directive found")}))}let f,h;function p(){for(let e in f)e in location&&(f[e]=location[e])}const m={get path(){return h.hash.slice(1)||"/"},get location(){return h},resolve(e){let t=new URL(e);return t.hash?t.hash.slice(1)||"/":t.pathname},navigate(e,t=!1){0>e.indexOf("#")&&(e="#"+e),g(e,t)}},d={get path(){return h.pathname},get location(){return h},resolve(e){return new URL(e).pathname},navigate(e,t=!1){g(e,t)}};function g(e,t){history[t?"replaceState":"pushState"]({},"",e),h.refresh()}const v={html5:d,hash:m};function w({$data:t,addScopeToNode:n,directive:a,magic:o,reactive:i}){a("router",((a,{modifiers:o,value:s},{cleanup:u})=>{s||(s="html5");const c=t(a).$router;if(c||"outlet"!==s&&"link"!==s)switch(s){case"outlet":c.outlet?e("x-router:outlet already specified",c.outlet,a):(c.outlet=a,u((()=>c.outlet=null)));break;case"link":!function(){let t=function(t){if($(t))return t;const n=t.querySelectorAll("a");return 1!==n.length&&e(`Expected exactly one link, but found ${n.length}`),n[0]}(a);if(t){a._r_routerlink=t;const e=(t.getAttribute("target")??"").indexOf("_blank")>=0,n=l(t,"click",(n=>{n.metaKey||n.altKey||n.ctrlKey||n.shiftKey||n.defaultPrevented||n.button>0||e||(n.preventDefault(),c.navigate(`${t.pathname}${t.search}${t.hash}`,(e=>e.includes("replace"))(o)))}));u(n)}}();break;default:!function(){if(r(a))return void e("x-router cannot be used on a 'template' tag");const t=i({pattern:"",path:"",params:{}}),o=function(t){h??=(f||(f=Alpine.reactive({hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",port:0,protocol:"",search:"",refresh(){p()}}),p(),l(window,"hashchange",p),l(window,"popstate",p)),f),t||="html5";let n=v[t];return n||(e(`Unknown history API: ${t}`),n=d),n}(s),c={routes:[],outlet:null,active:null,history:o,values:t,async match(e){for(let t of this.routes){const n=t.match(e);if(n){const r={router:c,route:t,params:n,path:e};if(!1!==await t.handler(r))return r}}},navigate(e,t=!1){return o.navigate(e,t),!0}};function m(){if(c.active){for(let e of c.active.nodes??[])e.remove();c.active.nodes=null,c.active=null}}n(a,{$route:t,$router:c});const g=function(e,t,n=null){const{effect:r,release:a}=Alpine;let o,i,l=!1;const s=r((()=>{o=e(),l||(n?.deep&&JSON.stringify(o),i=o),(l||(n?.immediate??1))&&setTimeout((()=>{t(o,i),i=o}),0),l=!0}));return()=>a(s)}((()=>o.path),(async e=>{const n=await c.match(e);n?e===o.path&&function(e,n,a){if(e.nodes?.length&&t.path===n)return;m(),t.path=n,t.pattern=e.template,t.params=a??{},c.active=e;const o=c.outlet;o&&e.view().then((i=>{t.path===n&&t.pattern===e.template&&JSON.stringify(t.params)===JSON.stringify(a)&&(e.nodes=[...i.cloneNode(!0).childNodes],r(o)?e.nodes.forEach((e=>o.parentElement.insertBefore(e,o))):e.nodes.forEach((e=>o.append(e))))}))}(n.route,n.path,n.params):m()}));u(g),u(m)}()}else e("no x-router directive found")})),o("active",(n=>{const r=t(n).$router;if(!r)return e("No x-router directive found"),!1;JSON.stringify(r.values);const a=$(n)?n:(e=>{for(;e&&!e._r_routerlink;)e=(e._x_teleportBack??e).parentElement;return e})(n)?._r_routerlink;return a?r.history.resolve(a.href)===r.values.path:(n._r_routerlink_init?e("x-router:link directive not found",n):queueMicrotask((()=>{n._r_routerlink_init=!0,r.values.path=r.values.path})),!1)}))}function $(e){return"A"===e.tagName.toUpperCase()}function _(e){window.RoutePattern=u,e.plugin([w,c])}document.addEventListener("alpine:init",(()=>{Alpine.plugin(_)}))}();
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ramstack/alpinegear-router",
|
3
|
-
"version": "1.1.0
|
3
|
+
"version": "1.1.0",
|
4
4
|
"description": "@ramstack/alpinegear-router provides routing-related directives for Alpine.js, enabling client-side navigation and routing functionality.",
|
5
5
|
"author": "Rameel Burhan",
|
6
6
|
"license": "MIT",
|