breeze-router 0.1.0 → 0.2.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Brandon Zhang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -43,6 +43,16 @@ ROUTER.add("/users/:username/posts/:postId", async ({ route, params }) => {
43
43
  ROUTER.start();
44
44
  ```
45
45
 
46
+ ## Util functions
47
+
48
+ ### `toggleParam(this, 1)`
49
+
50
+ ```html
51
+ <input type="checkbox" name="freelance" onclick="window.ROUTER.toggleParam(this, 1)">
52
+ ```
53
+
54
+ If checked, then it will append search params to url like this: `localhost/users?freelance=1`, if you click checkbox again, it will remove that search param from url.
55
+
46
56
  ## License
47
57
 
48
58
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -179,6 +179,11 @@ class BreezeRouter {
179
179
  // If no matching route found, route will be '404' route
180
180
  // which has been handled by _matchUrlToRoute already
181
181
  await this._handleRoute({ route, params });
182
+
183
+ /**
184
+ * Update previous route, so application route handler can check and decide if it should re-render whole page.
185
+ */
186
+ this._previousRoute = route;
182
187
  }
183
188
 
184
189
  /**
@@ -261,13 +266,37 @@ class BreezeRouter {
261
266
  }
262
267
 
263
268
  event.preventDefault();
264
- let href = anchor.getAttribute("href").trim();
265
- if (!href.startsWith("/")) {
269
+ let href = anchor.getAttribute("href")?.trim();
270
+ if (!href?.startsWith("/")) {
266
271
  href = "/" + href;
267
272
  }
268
273
 
269
274
  this.navigateTo(href);
270
275
  }
276
+
277
+ /**
278
+ * Add or remove search param to current url.
279
+ * @param {HTMLInputElement} checkbox
280
+ * @param {string} value
281
+ * @returns void
282
+ */
283
+ toggleParam(checkbox, value) {
284
+ const params = new URLSearchParams(location.search);
285
+ const name = checkbox.getAttribute('name');
286
+ if (!name) {
287
+ return console.warn(`name attribute is not set on ${checkbox.outerHTML}`);
288
+ }
289
+ if (checkbox.checked) {
290
+ !params.has(name) && params.set(name, value);
291
+ } else if (!checkbox.checked) {
292
+ params.has(name) && params.delete(name);
293
+ }
294
+
295
+ const newUrl = !!params.size
296
+ ? `${location.pathname}?${params.toString()}`
297
+ : location.pathname;
298
+ this.navigateTo(newUrl);
299
+ }
271
300
  }
272
301
 
273
302
  export { BreezeRouter as default };
@@ -1 +1 @@
1
- {"version":3,"file":"BreezeRouter.js","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["// @ts-check\n\n/**\n * Check if given param is function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"function\";\n};\n\n/**\n * Check if given param is async function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isAsyncFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a give url\n * @param {string} url\n * @returns {string}\n */\nexport const removeTrailingSlash = (url) => {\n if (url.endsWith(\"/\")) {\n url = url.replace(/\\/$/, \"\");\n }\n\n return url;\n};\n\n/**\n * Find anchor element from click event.\n * @param {Event} e - The click event.\n * @returns {HTMLAnchorElement|undefined}\n */\nexport const findAnchor = (e) => {\n return e.composedPath().find((elem) => {\n return elem.tagName === \"A\";\n });\n};\n\n/**\n * Check if the router should handle a click event on an anchor element.\n * @param {Event} e - The click event.\n * @param {HTMLAnchorElement} anchor - The anchor element.\n * @returns {boolean} - True if the router should handle the event, false otherwise.\n */\nexport const shouldRouterHandleClick = (e, anchor) => {\n // If the event has already been handled by another event listener\n if (e.defaultPrevented) {\n return false;\n }\n\n // If the user is holding down the meta, control, or shift key\n if (e.metaKey || e.ctrlKey || e.shiftKey) {\n return false;\n }\n\n if (!anchor) {\n return false;\n }\n\n if (anchor.target) {\n return false;\n }\n\n if (\n anchor.hasAttribute(\"download\") ||\n anchor.getAttribute(\"rel\") === \"external\"\n ) {\n return false;\n }\n\n const href = anchor.href;\n if (!href || href.startsWith(\"mailto:\")) {\n return false;\n }\n\n // If the href attribute does not start with the same origin\n if (!href.startsWith(location.origin)) {\n return false;\n }\n\n return true;\n};\n","// @ts-check\nimport {\n isFunction,\n isAsyncFunction,\n removeTrailingSlash,\n findAnchor,\n shouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n /**\n * Creates a new BreezeRouter instance.\n * @constructor\n */\n constructor() {\n /**\n * Object containing all registered routes.\n * @type {import('./types.js').Route}\n * @private\n */\n this._routes = {};\n\n /**\n * The previous route that was navigated to\n * @type {import('./types.js').Route}\n * @private\n */\n this._previousRoute = {};\n\n // Bind event listeners\n window.addEventListener(\"popstate\", this._onChanged.bind(this));\n document.body.addEventListener(\"click\", this._handleClick.bind(this));\n }\n\n /**\n * Starts the router.\n * @returns {void}\n */\n start() {\n this._onChanged();\n }\n\n /**\n * Adds a new route to the router.\n * @param {string} route - The route path to add.\n * @param {Function} handler - The async function to handle the route\n * @returns {BreezeRouter|void} The BreezeRouter instance.\n */\n add(route, handler) {\n route = route.trim();\n if (route !== \"/\") {\n route = removeTrailingSlash(route.trim());\n }\n\n if (this._routes[route]) {\n return console.warn(`Route already exists: ${route}`);\n }\n\n if (typeof handler !== \"function\") {\n return console.error(`handler on route '${route}' is not a function.`);\n }\n\n this._routes[route] = {\n path: route,\n handler,\n };\n\n return this;\n }\n\n /**\n * Navigates to the specified URL.\n * @param {string} url - The URL to navigate to\n * @returns {void}\n */\n navigateTo(url) {\n window.history.pushState({ url }, \"\", url);\n this._onChanged();\n }\n\n /**\n * Redirects a URL\n * @param {string} url\n * @returns {void}\n */\n redirect(url) {\n this.navigateTo(url);\n }\n\n async _onChanged() {\n const path = window.location.pathname;\n const { route, params } = this._matchUrlToRoute(path);\n\n // If no matching route found, route will be '404' route\n // which has been handled by _matchUrlToRoute already\n await this._handleRoute({ route, params });\n }\n\n /**\n * Processes route callbacks registered by app\n * @param {import('./types.js').MatchedRoute} options\n * @returns {Promise<void>}\n */\n async _handleRoute({ route, params }) {\n if (isFunction(route.handler)) {\n route.handler({ route, params });\n }\n\n if (isAsyncFunction(route.handler)) {\n await route.handler({ route, params });\n }\n }\n\n /**\n *\n * @param {string} url - Current url users visite or nagivate to.\n * @returns {import('./types.js').MatchedRoute}\n */\n _matchUrlToRoute(url) {\n /** @type {import('./types.js').RouteParams} */\n const params = {};\n\n if (url !== \"/\") {\n url = removeTrailingSlash(url);\n }\n\n const matchedRoute = Object.keys(this._routes).find((route) => {\n if (url.split(\"/\").length !== route.split(\"/\").length) {\n return false;\n }\n\n let routeSegments = route.split(\"/\").slice(1);\n let urlSegments = url.split(\"/\").slice(1);\n\n // If each segment in the url matches the corresponding segment in the route path,\n // or the route path segment starts with a ':' then the route is matched.\n const match = routeSegments.every((segment, i) => {\n return segment === urlSegments[i] || segment.startsWith(\":\");\n });\n\n if (!match) {\n return false;\n }\n\n // If the route matches the URL, pull out any params from the URL.\n routeSegments.forEach((segment, i) => {\n if (segment.startsWith(\":\")) {\n const propName = segment.slice(1);\n params[propName] = decodeURIComponent(urlSegments[i]);\n }\n });\n\n return true;\n });\n\n if (matchedRoute) {\n return { route: this._routes[matchedRoute], params };\n } else {\n return { route: this._routes[404], params };\n }\n }\n\n /**\n * Handles <a> link clicks\n * @param {Event} event\n * @returns {void}\n */\n _handleClick(event) {\n const anchor = findAnchor(event);\n if (!anchor) {\n return;\n }\n\n if (!shouldRouterHandleClick(event, anchor)) {\n return;\n }\n\n event.preventDefault();\n let href = anchor.getAttribute(\"href\").trim();\n if (!href.startsWith(\"/\")) {\n href = \"/\" + href;\n }\n\n this.navigateTo(href);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BreezeRouter.js","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["// @ts-check\n\n/**\n * Check if given param is function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"function\";\n};\n\n/**\n * Check if given param is async function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isAsyncFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a give url\n * @param {string} url\n * @returns {string}\n */\nexport const removeTrailingSlash = (url) => {\n if (url.endsWith(\"/\")) {\n url = url.replace(/\\/$/, \"\");\n }\n\n return url;\n};\n\n/**\n * Find anchor element from click event.\n * @param {Event} e - The click event.\n * @returns {HTMLAnchorElement|undefined}\n */\nexport const findAnchor = (e) => {\n return e.composedPath().find((elem) => {\n return elem.tagName === \"A\";\n });\n};\n\n/**\n * Check if the router should handle a click event on an anchor element.\n * @param {Event} e - The click event.\n * @param {HTMLAnchorElement} anchor - The anchor element.\n * @returns {boolean} - True if the router should handle the event, false otherwise.\n */\nexport const shouldRouterHandleClick = (e, anchor) => {\n // If the event has already been handled by another event listener\n if (e.defaultPrevented) {\n return false;\n }\n\n // If the user is holding down the meta, control, or shift key\n if (e.metaKey || e.ctrlKey || e.shiftKey) {\n return false;\n }\n\n if (!anchor) {\n return false;\n }\n\n if (anchor.target) {\n return false;\n }\n\n if (\n anchor.hasAttribute(\"download\") ||\n anchor.getAttribute(\"rel\") === \"external\"\n ) {\n return false;\n }\n\n const href = anchor.href;\n if (!href || href.startsWith(\"mailto:\")) {\n return false;\n }\n\n // If the href attribute does not start with the same origin\n if (!href.startsWith(location.origin)) {\n return false;\n }\n\n return true;\n};\n","// @ts-check\nimport {\n isFunction,\n isAsyncFunction,\n removeTrailingSlash,\n findAnchor,\n shouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n /**\n * Creates a new BreezeRouter instance.\n * @constructor\n */\n constructor() {\n /**\n * Object containing all registered routes.\n * @type {import('./types.js').Route}\n * @private\n */\n this._routes = {};\n\n /**\n * The previous route that was navigated to\n * @type {import('./types.js').Route}\n * @private\n */\n this._previousRoute = {};\n\n // Bind event listeners\n window.addEventListener(\"popstate\", this._onChanged.bind(this));\n document.body.addEventListener(\"click\", this._handleClick.bind(this));\n }\n\n /**\n * Starts the router.\n * @returns {void}\n */\n start() {\n this._onChanged();\n }\n\n /**\n * Adds a new route to the router.\n * @param {string} route - The route path to add.\n * @param {Function} handler - The async function to handle the route\n * @returns {BreezeRouter|void} The BreezeRouter instance.\n */\n add(route, handler) {\n route = route.trim();\n if (route !== \"/\") {\n route = removeTrailingSlash(route.trim());\n }\n\n if (this._routes[route]) {\n return console.warn(`Route already exists: ${route}`);\n }\n\n if (typeof handler !== \"function\") {\n return console.error(`handler on route '${route}' is not a function.`);\n }\n\n this._routes[route] = {\n path: route,\n handler,\n };\n\n return this;\n }\n\n /**\n * Navigates to the specified URL.\n * @param {string} url - The URL to navigate to\n * @returns {void}\n */\n navigateTo(url) {\n window.history.pushState({ url }, \"\", url);\n this._onChanged();\n }\n\n /**\n * Redirects a URL\n * @param {string} url\n * @returns {void}\n */\n redirect(url) {\n this.navigateTo(url);\n }\n\n async _onChanged() {\n const path = window.location.pathname;\n const { route, params } = this._matchUrlToRoute(path);\n\n // If no matching route found, route will be '404' route\n // which has been handled by _matchUrlToRoute already\n await this._handleRoute({ route, params });\n\n /**\n * Update previous route, so application route handler can check and decide if it should re-render whole page.\n */\n this._previousRoute = route;\n }\n\n /**\n * Processes route callbacks registered by app\n * @param {import('./types.js').MatchedRoute} options\n * @returns {Promise<void>}\n */\n async _handleRoute({ route, params }) {\n if (isFunction(route.handler)) {\n route.handler({ route, params });\n }\n\n if (isAsyncFunction(route.handler)) {\n await route.handler({ route, params });\n }\n }\n\n /**\n *\n * @param {string} url - Current url users visite or nagivate to.\n * @returns {import('./types.js').MatchedRoute}\n */\n _matchUrlToRoute(url) {\n /** @type {import('./types.js').RouteParams} */\n const params = {};\n\n if (url !== \"/\") {\n url = removeTrailingSlash(url);\n }\n\n const matchedRoute = Object.keys(this._routes).find((route) => {\n if (url.split(\"/\").length !== route.split(\"/\").length) {\n return false;\n }\n\n let routeSegments = route.split(\"/\").slice(1);\n let urlSegments = url.split(\"/\").slice(1);\n\n // If each segment in the url matches the corresponding segment in the route path,\n // or the route path segment starts with a ':' then the route is matched.\n const match = routeSegments.every((segment, i) => {\n return segment === urlSegments[i] || segment.startsWith(\":\");\n });\n\n if (!match) {\n return false;\n }\n\n // If the route matches the URL, pull out any params from the URL.\n routeSegments.forEach((segment, i) => {\n if (segment.startsWith(\":\")) {\n const propName = segment.slice(1);\n params[propName] = decodeURIComponent(urlSegments[i]);\n }\n });\n\n return true;\n });\n\n if (matchedRoute) {\n return { route: this._routes[matchedRoute], params };\n } else {\n return { route: this._routes[404], params };\n }\n }\n\n /**\n * Handles <a> link clicks\n * @param {Event} event\n * @returns {void}\n */\n _handleClick(event) {\n const anchor = findAnchor(event);\n if (!anchor) {\n return;\n }\n\n if (!shouldRouterHandleClick(event, anchor)) {\n return;\n }\n\n event.preventDefault();\n let href = anchor.getAttribute(\"href\")?.trim();\n if (!href?.startsWith(\"/\")) {\n href = \"/\" + href;\n }\n\n this.navigateTo(href);\n }\n\n /**\n * Add or remove search param to current url.\n * @param {HTMLInputElement} checkbox \n * @param {string} value\n * @returns void\n */\n toggleParam(checkbox, value) {\n const params = new URLSearchParams(location.search);\n const name = checkbox.getAttribute('name')\n if (!name) {\n return console.warn(`name attribute is not set on ${checkbox.outerHTML}`);\n }\n if (checkbox.checked) {\n !params.has(name) && params.set(name, value);\n } else if (!checkbox.checked) {\n params.has(name) && params.delete(name);\n }\n \n const newUrl = !!params.size\n ? `${location.pathname}?${params.toString()}`\n : location.pathname;\n this.navigateTo(newUrl);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const t=t=>(t.endsWith("/")&&(t=t.replace(/\/$/,"")),t);class e{constructor(){this._routes={},this._previousRoute={},window.addEventListener("popstate",this._onChanged.bind(this)),document.body.addEventListener("click",this._handleClick.bind(this))}start(){this._onChanged()}add(e,r){return"/"!==(e=e.trim())&&(e=t(e.trim())),this._routes[e]?console.warn(`Route already exists: ${e}`):"function"!=typeof r?console.error(`handler on route '${e}' is not a function.`):(this._routes[e]={path:e,handler:r},this)}navigateTo(t){window.history.pushState({url:t},"",t),this._onChanged()}redirect(t){this.navigateTo(t)}async _onChanged(){const t=window.location.pathname,{route:e,params:r}=this._matchUrlToRoute(t);await this._handleRoute({route:e,params:r})}async _handleRoute({route:t,params:e}){"function"===t.handler.constructor.name.toLowerCase()&&t.handler({route:t,params:e}),(t=>"asyncfunction"===t.constructor.name.toLowerCase())(t.handler)&&await t.handler({route:t,params:e})}_matchUrlToRoute(e){const r={};"/"!==e&&(e=t(e));const n=Object.keys(this._routes).find((t=>{if(e.split("/").length!==t.split("/").length)return!1;let n=t.split("/").slice(1),a=e.split("/").slice(1);return!!n.every(((t,e)=>t===a[e]||t.startsWith(":")))&&(n.forEach(((t,e)=>{if(t.startsWith(":")){const n=t.slice(1);r[n]=decodeURIComponent(a[e])}})),!0)}));return n?{route:this._routes[n],params:r}:{route:this._routes[404],params:r}}_handleClick(t){const e=t.composedPath().find((t=>"A"===t.tagName));if(!e)return;if(!((t,e)=>{if(t.defaultPrevented)return!1;if(t.metaKey||t.ctrlKey||t.shiftKey)return!1;if(!e)return!1;if(e.target)return!1;if(e.hasAttribute("download")||"external"===e.getAttribute("rel"))return!1;const r=e.href;return!(!r||r.startsWith("mailto:")||!r.startsWith(location.origin))})(t,e))return;t.preventDefault();let r=e.getAttribute("href").trim();r.startsWith("/")||(r="/"+r),this.navigateTo(r)}}export{e as default};
1
+ const t=t=>(t.endsWith("/")&&(t=t.replace(/\/$/,"")),t);class e{constructor(){this._routes={},this._previousRoute={},window.addEventListener("popstate",this._onChanged.bind(this)),document.body.addEventListener("click",this._handleClick.bind(this))}start(){this._onChanged()}add(e,r){return"/"!==(e=e.trim())&&(e=t(e.trim())),this._routes[e]?console.warn(`Route already exists: ${e}`):"function"!=typeof r?console.error(`handler on route '${e}' is not a function.`):(this._routes[e]={path:e,handler:r},this)}navigateTo(t){window.history.pushState({url:t},"",t),this._onChanged()}redirect(t){this.navigateTo(t)}async _onChanged(){const t=window.location.pathname,{route:e,params:r}=this._matchUrlToRoute(t);await this._handleRoute({route:e,params:r}),this._previousRoute=e}async _handleRoute({route:t,params:e}){"function"===t.handler.constructor.name.toLowerCase()&&t.handler({route:t,params:e}),(t=>"asyncfunction"===t.constructor.name.toLowerCase())(t.handler)&&await t.handler({route:t,params:e})}_matchUrlToRoute(e){const r={};"/"!==e&&(e=t(e));const a=Object.keys(this._routes).find((t=>{if(e.split("/").length!==t.split("/").length)return!1;let a=t.split("/").slice(1),n=e.split("/").slice(1);return!!a.every(((t,e)=>t===n[e]||t.startsWith(":")))&&(a.forEach(((t,e)=>{if(t.startsWith(":")){const a=t.slice(1);r[a]=decodeURIComponent(n[e])}})),!0)}));return a?{route:this._routes[a],params:r}:{route:this._routes[404],params:r}}_handleClick(t){const e=t.composedPath().find((t=>"A"===t.tagName));if(!e)return;if(!((t,e)=>{if(t.defaultPrevented)return!1;if(t.metaKey||t.ctrlKey||t.shiftKey)return!1;if(!e)return!1;if(e.target)return!1;if(e.hasAttribute("download")||"external"===e.getAttribute("rel"))return!1;const r=e.href;return!(!r||r.startsWith("mailto:")||!r.startsWith(location.origin))})(t,e))return;t.preventDefault();let r=e.getAttribute("href")?.trim();r?.startsWith("/")||(r="/"+r),this.navigateTo(r)}toggleParam(t,e){const r=new URLSearchParams(location.search),a=t.getAttribute("name");if(!a)return console.warn(`name attribute is not set on ${t.outerHTML}`);t.checked?!r.has(a)&&r.set(a,e):t.checked||r.has(a)&&r.delete(a);const n=r.size?`${location.pathname}?${r.toString()}`:location.pathname;this.navigateTo(n)}}export{e as default};
2
2
  //# sourceMappingURL=BreezeRouter.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BreezeRouter.min.js","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["// @ts-check\n\n/**\n * Check if given param is function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"function\";\n};\n\n/**\n * Check if given param is async function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isAsyncFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a give url\n * @param {string} url\n * @returns {string}\n */\nexport const removeTrailingSlash = (url) => {\n if (url.endsWith(\"/\")) {\n url = url.replace(/\\/$/, \"\");\n }\n\n return url;\n};\n\n/**\n * Find anchor element from click event.\n * @param {Event} e - The click event.\n * @returns {HTMLAnchorElement|undefined}\n */\nexport const findAnchor = (e) => {\n return e.composedPath().find((elem) => {\n return elem.tagName === \"A\";\n });\n};\n\n/**\n * Check if the router should handle a click event on an anchor element.\n * @param {Event} e - The click event.\n * @param {HTMLAnchorElement} anchor - The anchor element.\n * @returns {boolean} - True if the router should handle the event, false otherwise.\n */\nexport const shouldRouterHandleClick = (e, anchor) => {\n // If the event has already been handled by another event listener\n if (e.defaultPrevented) {\n return false;\n }\n\n // If the user is holding down the meta, control, or shift key\n if (e.metaKey || e.ctrlKey || e.shiftKey) {\n return false;\n }\n\n if (!anchor) {\n return false;\n }\n\n if (anchor.target) {\n return false;\n }\n\n if (\n anchor.hasAttribute(\"download\") ||\n anchor.getAttribute(\"rel\") === \"external\"\n ) {\n return false;\n }\n\n const href = anchor.href;\n if (!href || href.startsWith(\"mailto:\")) {\n return false;\n }\n\n // If the href attribute does not start with the same origin\n if (!href.startsWith(location.origin)) {\n return false;\n }\n\n return true;\n};\n","// @ts-check\nimport {\n isFunction,\n isAsyncFunction,\n removeTrailingSlash,\n findAnchor,\n shouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n /**\n * Creates a new BreezeRouter instance.\n * @constructor\n */\n constructor() {\n /**\n * Object containing all registered routes.\n * @type {import('./types.js').Route}\n * @private\n */\n this._routes = {};\n\n /**\n * The previous route that was navigated to\n * @type {import('./types.js').Route}\n * @private\n */\n this._previousRoute = {};\n\n // Bind event listeners\n window.addEventListener(\"popstate\", this._onChanged.bind(this));\n document.body.addEventListener(\"click\", this._handleClick.bind(this));\n }\n\n /**\n * Starts the router.\n * @returns {void}\n */\n start() {\n this._onChanged();\n }\n\n /**\n * Adds a new route to the router.\n * @param {string} route - The route path to add.\n * @param {Function} handler - The async function to handle the route\n * @returns {BreezeRouter|void} The BreezeRouter instance.\n */\n add(route, handler) {\n route = route.trim();\n if (route !== \"/\") {\n route = removeTrailingSlash(route.trim());\n }\n\n if (this._routes[route]) {\n return console.warn(`Route already exists: ${route}`);\n }\n\n if (typeof handler !== \"function\") {\n return console.error(`handler on route '${route}' is not a function.`);\n }\n\n this._routes[route] = {\n path: route,\n handler,\n };\n\n return this;\n }\n\n /**\n * Navigates to the specified URL.\n * @param {string} url - The URL to navigate to\n * @returns {void}\n */\n navigateTo(url) {\n window.history.pushState({ url }, \"\", url);\n this._onChanged();\n }\n\n /**\n * Redirects a URL\n * @param {string} url\n * @returns {void}\n */\n redirect(url) {\n this.navigateTo(url);\n }\n\n async _onChanged() {\n const path = window.location.pathname;\n const { route, params } = this._matchUrlToRoute(path);\n\n // If no matching route found, route will be '404' route\n // which has been handled by _matchUrlToRoute already\n await this._handleRoute({ route, params });\n }\n\n /**\n * Processes route callbacks registered by app\n * @param {import('./types.js').MatchedRoute} options\n * @returns {Promise<void>}\n */\n async _handleRoute({ route, params }) {\n if (isFunction(route.handler)) {\n route.handler({ route, params });\n }\n\n if (isAsyncFunction(route.handler)) {\n await route.handler({ route, params });\n }\n }\n\n /**\n *\n * @param {string} url - Current url users visite or nagivate to.\n * @returns {import('./types.js').MatchedRoute}\n */\n _matchUrlToRoute(url) {\n /** @type {import('./types.js').RouteParams} */\n const params = {};\n\n if (url !== \"/\") {\n url = removeTrailingSlash(url);\n }\n\n const matchedRoute = Object.keys(this._routes).find((route) => {\n if (url.split(\"/\").length !== route.split(\"/\").length) {\n return false;\n }\n\n let routeSegments = route.split(\"/\").slice(1);\n let urlSegments = url.split(\"/\").slice(1);\n\n // If each segment in the url matches the corresponding segment in the route path,\n // or the route path segment starts with a ':' then the route is matched.\n const match = routeSegments.every((segment, i) => {\n return segment === urlSegments[i] || segment.startsWith(\":\");\n });\n\n if (!match) {\n return false;\n }\n\n // If the route matches the URL, pull out any params from the URL.\n routeSegments.forEach((segment, i) => {\n if (segment.startsWith(\":\")) {\n const propName = segment.slice(1);\n params[propName] = decodeURIComponent(urlSegments[i]);\n }\n });\n\n return true;\n });\n\n if (matchedRoute) {\n return { route: this._routes[matchedRoute], params };\n } else {\n return { route: this._routes[404], params };\n }\n }\n\n /**\n * Handles <a> link clicks\n * @param {Event} event\n * @returns {void}\n */\n _handleClick(event) {\n const anchor = findAnchor(event);\n if (!anchor) {\n return;\n }\n\n if (!shouldRouterHandleClick(event, anchor)) {\n return;\n }\n\n event.preventDefault();\n let href = anchor.getAttribute(\"href\").trim();\n if (!href.startsWith(\"/\")) {\n href = \"/\" + href;\n }\n\n this.navigateTo(href);\n }\n}\n"],"names":["removeTrailingSlash","url","endsWith","replace","BreezeRouter","constructor","this","_routes","_previousRoute","window","addEventListener","_onChanged","bind","document","body","_handleClick","start","add","route","handler","trim","console","warn","error","path","navigateTo","history","pushState","redirect","async","location","pathname","params","_matchUrlToRoute","_handleRoute","name","toLowerCase","fn","isAsyncFunction","matchedRoute","Object","keys","find","split","length","routeSegments","slice","urlSegments","every","segment","i","startsWith","forEach","propName","decodeURIComponent","event","anchor","composedPath","elem","tagName","e","defaultPrevented","metaKey","ctrlKey","shiftKey","target","hasAttribute","getAttribute","href","origin","shouldRouterHandleClick","preventDefault"],"mappings":"AAOO,MAkBMA,EAAuBC,IAC9BA,EAAIC,SAAS,OACfD,EAAMA,EAAIE,QAAQ,MAAO,KAGpBF,GClBM,MAAMG,EAKnBC,cAMEC,KAAKC,QAAU,GAOfD,KAAKE,eAAiB,GAGtBC,OAAOC,iBAAiB,WAAYJ,KAAKK,WAAWC,KAAKN,OACzDO,SAASC,KAAKJ,iBAAiB,QAASJ,KAAKS,aAAaH,KAAKN,MAChE,CAMDU,QACEV,KAAKK,YACN,CAQDM,IAAIC,EAAOC,GAMT,MAJc,OADdD,EAAQA,EAAME,UAEZF,EAAQlB,EAAoBkB,EAAME,SAGhCd,KAAKC,QAAQW,GACRG,QAAQC,KAAK,yBAAyBJ,KAGxB,mBAAZC,EACFE,QAAQE,MAAM,qBAAqBL,0BAG5CZ,KAAKC,QAAQW,GAAS,CACpBM,KAAMN,EACNC,WAGKb,KACR,CAODmB,WAAWxB,GACTQ,OAAOiB,QAAQC,UAAU,CAAE1B,OAAO,GAAIA,GACtCK,KAAKK,YACN,CAODiB,SAAS3B,GACPK,KAAKmB,WAAWxB,EACjB,CAED4B,mBACE,MAAML,EAAOf,OAAOqB,SAASC,UACvBb,MAAEA,EAAKc,OAAEA,GAAW1B,KAAK2B,iBAAiBT,SAI1ClB,KAAK4B,aAAa,CAAEhB,QAAOc,UAClC,CAODH,oBAAmBX,MAAEA,EAAKc,OAAEA,IDlGiB,aCmG5Bd,EAAMC,QDnGbd,YAAY8B,KAAKC,eCoGvBlB,EAAMC,QAAQ,CAAED,QAAOc,WD5FE,CAACK,GACe,kBAAtCA,EAAGhC,YAAY8B,KAAKC,cC8FrBE,CAAgBpB,EAAMC,gBAClBD,EAAMC,QAAQ,CAAED,QAAOc,UAEhC,CAODC,iBAAiBhC,GAEf,MAAM+B,EAAS,CAAA,EAEH,MAAR/B,IACFA,EAAMD,EAAoBC,IAG5B,MAAMsC,EAAeC,OAAOC,KAAKnC,KAAKC,SAASmC,MAAMxB,IACnD,GAAIjB,EAAI0C,MAAM,KAAKC,SAAW1B,EAAMyB,MAAM,KAAKC,OAC7C,OAAO,EAGT,IAAIC,EAAgB3B,EAAMyB,MAAM,KAAKG,MAAM,GACvCC,EAAc9C,EAAI0C,MAAM,KAAKG,MAAM,GAQvC,QAJcD,EAAcG,OAAM,CAACC,EAASC,IACnCD,IAAYF,EAAYG,IAAMD,EAAQE,WAAW,SAQ1DN,EAAcO,SAAQ,CAACH,EAASC,KAC9B,GAAID,EAAQE,WAAW,KAAM,CAC3B,MAAME,EAAWJ,EAAQH,MAAM,GAC/Bd,EAAOqB,GAAYC,mBAAmBP,EAAYG,GACnD,MAGI,EAAI,IAGb,OAAIX,EACK,CAAErB,MAAOZ,KAAKC,QAAQgC,GAAeP,UAErC,CAAEd,MAAOZ,KAAKC,QAAQ,KAAMyB,SAEtC,CAODjB,aAAawC,GACX,MAAMC,EAAoBD,EDpInBE,eAAef,MAAMgB,GACJ,MAAjBA,EAAKC,UCoIZ,IAAKH,EACH,OAGF,ID9HmC,EAACI,EAAGJ,KAEzC,GAAII,EAAEC,iBACJ,OAAO,EAIT,GAAID,EAAEE,SAAWF,EAAEG,SAAWH,EAAEI,SAC9B,OAAO,EAGT,IAAKR,EACH,OAAO,EAGT,GAAIA,EAAOS,OACT,OAAO,EAGT,GACET,EAAOU,aAAa,aACW,aAA/BV,EAAOW,aAAa,OAEpB,OAAO,EAGT,MAAMC,EAAOZ,EAAOY,KACpB,SAAKA,GAAQA,EAAKjB,WAAW,aAKxBiB,EAAKjB,WAAWrB,SAASuC,QAInB,EC0FJC,CAAwBf,EAAOC,GAClC,OAGFD,EAAMgB,iBACN,IAAIH,EAAOZ,EAAOW,aAAa,QAAQ/C,OAClCgD,EAAKjB,WAAW,OACnBiB,EAAO,IAAMA,GAGf9D,KAAKmB,WAAW2C,EACjB"}
1
+ {"version":3,"file":"BreezeRouter.min.js","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["// @ts-check\n\n/**\n * Check if given param is function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"function\";\n};\n\n/**\n * Check if given param is async function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isAsyncFunction = (fn) => {\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a give url\n * @param {string} url\n * @returns {string}\n */\nexport const removeTrailingSlash = (url) => {\n if (url.endsWith(\"/\")) {\n url = url.replace(/\\/$/, \"\");\n }\n\n return url;\n};\n\n/**\n * Find anchor element from click event.\n * @param {Event} e - The click event.\n * @returns {HTMLAnchorElement|undefined}\n */\nexport const findAnchor = (e) => {\n return e.composedPath().find((elem) => {\n return elem.tagName === \"A\";\n });\n};\n\n/**\n * Check if the router should handle a click event on an anchor element.\n * @param {Event} e - The click event.\n * @param {HTMLAnchorElement} anchor - The anchor element.\n * @returns {boolean} - True if the router should handle the event, false otherwise.\n */\nexport const shouldRouterHandleClick = (e, anchor) => {\n // If the event has already been handled by another event listener\n if (e.defaultPrevented) {\n return false;\n }\n\n // If the user is holding down the meta, control, or shift key\n if (e.metaKey || e.ctrlKey || e.shiftKey) {\n return false;\n }\n\n if (!anchor) {\n return false;\n }\n\n if (anchor.target) {\n return false;\n }\n\n if (\n anchor.hasAttribute(\"download\") ||\n anchor.getAttribute(\"rel\") === \"external\"\n ) {\n return false;\n }\n\n const href = anchor.href;\n if (!href || href.startsWith(\"mailto:\")) {\n return false;\n }\n\n // If the href attribute does not start with the same origin\n if (!href.startsWith(location.origin)) {\n return false;\n }\n\n return true;\n};\n","// @ts-check\nimport {\n isFunction,\n isAsyncFunction,\n removeTrailingSlash,\n findAnchor,\n shouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n /**\n * Creates a new BreezeRouter instance.\n * @constructor\n */\n constructor() {\n /**\n * Object containing all registered routes.\n * @type {import('./types.js').Route}\n * @private\n */\n this._routes = {};\n\n /**\n * The previous route that was navigated to\n * @type {import('./types.js').Route}\n * @private\n */\n this._previousRoute = {};\n\n // Bind event listeners\n window.addEventListener(\"popstate\", this._onChanged.bind(this));\n document.body.addEventListener(\"click\", this._handleClick.bind(this));\n }\n\n /**\n * Starts the router.\n * @returns {void}\n */\n start() {\n this._onChanged();\n }\n\n /**\n * Adds a new route to the router.\n * @param {string} route - The route path to add.\n * @param {Function} handler - The async function to handle the route\n * @returns {BreezeRouter|void} The BreezeRouter instance.\n */\n add(route, handler) {\n route = route.trim();\n if (route !== \"/\") {\n route = removeTrailingSlash(route.trim());\n }\n\n if (this._routes[route]) {\n return console.warn(`Route already exists: ${route}`);\n }\n\n if (typeof handler !== \"function\") {\n return console.error(`handler on route '${route}' is not a function.`);\n }\n\n this._routes[route] = {\n path: route,\n handler,\n };\n\n return this;\n }\n\n /**\n * Navigates to the specified URL.\n * @param {string} url - The URL to navigate to\n * @returns {void}\n */\n navigateTo(url) {\n window.history.pushState({ url }, \"\", url);\n this._onChanged();\n }\n\n /**\n * Redirects a URL\n * @param {string} url\n * @returns {void}\n */\n redirect(url) {\n this.navigateTo(url);\n }\n\n async _onChanged() {\n const path = window.location.pathname;\n const { route, params } = this._matchUrlToRoute(path);\n\n // If no matching route found, route will be '404' route\n // which has been handled by _matchUrlToRoute already\n await this._handleRoute({ route, params });\n\n /**\n * Update previous route, so application route handler can check and decide if it should re-render whole page.\n */\n this._previousRoute = route;\n }\n\n /**\n * Processes route callbacks registered by app\n * @param {import('./types.js').MatchedRoute} options\n * @returns {Promise<void>}\n */\n async _handleRoute({ route, params }) {\n if (isFunction(route.handler)) {\n route.handler({ route, params });\n }\n\n if (isAsyncFunction(route.handler)) {\n await route.handler({ route, params });\n }\n }\n\n /**\n *\n * @param {string} url - Current url users visite or nagivate to.\n * @returns {import('./types.js').MatchedRoute}\n */\n _matchUrlToRoute(url) {\n /** @type {import('./types.js').RouteParams} */\n const params = {};\n\n if (url !== \"/\") {\n url = removeTrailingSlash(url);\n }\n\n const matchedRoute = Object.keys(this._routes).find((route) => {\n if (url.split(\"/\").length !== route.split(\"/\").length) {\n return false;\n }\n\n let routeSegments = route.split(\"/\").slice(1);\n let urlSegments = url.split(\"/\").slice(1);\n\n // If each segment in the url matches the corresponding segment in the route path,\n // or the route path segment starts with a ':' then the route is matched.\n const match = routeSegments.every((segment, i) => {\n return segment === urlSegments[i] || segment.startsWith(\":\");\n });\n\n if (!match) {\n return false;\n }\n\n // If the route matches the URL, pull out any params from the URL.\n routeSegments.forEach((segment, i) => {\n if (segment.startsWith(\":\")) {\n const propName = segment.slice(1);\n params[propName] = decodeURIComponent(urlSegments[i]);\n }\n });\n\n return true;\n });\n\n if (matchedRoute) {\n return { route: this._routes[matchedRoute], params };\n } else {\n return { route: this._routes[404], params };\n }\n }\n\n /**\n * Handles <a> link clicks\n * @param {Event} event\n * @returns {void}\n */\n _handleClick(event) {\n const anchor = findAnchor(event);\n if (!anchor) {\n return;\n }\n\n if (!shouldRouterHandleClick(event, anchor)) {\n return;\n }\n\n event.preventDefault();\n let href = anchor.getAttribute(\"href\")?.trim();\n if (!href?.startsWith(\"/\")) {\n href = \"/\" + href;\n }\n\n this.navigateTo(href);\n }\n\n /**\n * Add or remove search param to current url.\n * @param {HTMLInputElement} checkbox \n * @param {string} value\n * @returns void\n */\n toggleParam(checkbox, value) {\n const params = new URLSearchParams(location.search);\n const name = checkbox.getAttribute('name')\n if (!name) {\n return console.warn(`name attribute is not set on ${checkbox.outerHTML}`);\n }\n if (checkbox.checked) {\n !params.has(name) && params.set(name, value);\n } else if (!checkbox.checked) {\n params.has(name) && params.delete(name);\n }\n \n const newUrl = !!params.size\n ? `${location.pathname}?${params.toString()}`\n : location.pathname;\n this.navigateTo(newUrl);\n }\n}\n"],"names":["removeTrailingSlash","url","endsWith","replace","BreezeRouter","constructor","this","_routes","_previousRoute","window","addEventListener","_onChanged","bind","document","body","_handleClick","start","add","route","handler","trim","console","warn","error","path","navigateTo","history","pushState","redirect","async","location","pathname","params","_matchUrlToRoute","_handleRoute","name","toLowerCase","fn","isAsyncFunction","matchedRoute","Object","keys","find","split","length","routeSegments","slice","urlSegments","every","segment","i","startsWith","forEach","propName","decodeURIComponent","event","anchor","composedPath","elem","tagName","e","defaultPrevented","metaKey","ctrlKey","shiftKey","target","hasAttribute","getAttribute","href","origin","shouldRouterHandleClick","preventDefault","toggleParam","checkbox","value","URLSearchParams","search","outerHTML","checked","has","set","delete","newUrl","size","toString"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "breeze-router",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "A lightweight, zero-dependency client-side router for single page applications (SPAs).",
5
5
  "main": "index.js",
6
6
  "type": "module",