breeze-router 0.4.0 → 1.0.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/README.md CHANGED
@@ -17,7 +17,7 @@ npm install breeze-router
17
17
  ```javascript
18
18
  <script type="module">
19
19
  import BreezeRouter from
20
- "https://unpkg.com/breeze-router@0.4.0/dist/BreezeRouter.min.js";
20
+ "https://unpkg.com/breeze-router@1.0.1/dist/BreezeRouter.min.js";
21
21
  </script>
22
22
  ```
23
23
 
@@ -64,6 +64,13 @@ ROUTER.add("/users/:username/posts/:postId", async ({ route, params }) => {
64
64
 
65
65
  // Call the `start` method to start the router.
66
66
  ROUTER.start();
67
+
68
+ // You can register the callbacks to run when the router is ready.
69
+ // This is a good time to do some initial work like injecting some
70
+ // global headers or footers into the page.
71
+ ROUTER.ready(() => {
72
+ console.log("ROUTER is ready now.");
73
+ });
67
74
  ```
68
75
 
69
76
  ## License
@@ -1,5 +1,4 @@
1
1
  // @ts-check
2
-
3
2
  /**
4
3
  * Check if given param is function
5
4
  * @param {Function} fn
@@ -102,6 +101,26 @@ class BreezeRouter {
102
101
  * @type {string}
103
102
  */
104
103
  #ignoreAttribute = "data-breeze-ignore";
104
+ #isReady = false;
105
+ #isReadyCallbacks = [];
106
+
107
+ previousPath = "";
108
+ /**
109
+ * The previous route that was navigated to.
110
+ *
111
+ * @type {import('./types.js').Route|null}
112
+ */
113
+ previousRoute = null;
114
+ previousParams = {};
115
+
116
+ currentPath = "";
117
+ /**
118
+ * The current route that we're on.
119
+ *
120
+ * @type {import('./types.js').Route|null}
121
+ */
122
+ currentRoute = null;
123
+ currentParams = {};
105
124
 
106
125
  /**
107
126
  * Creates a new BreezeRouter instance.
@@ -122,13 +141,6 @@ class BreezeRouter {
122
141
  */
123
142
  this._routes = {};
124
143
 
125
- /**
126
- * The previous route that was navigated to
127
- * @type {import('./types.js').Route|null}
128
- * @private
129
- */
130
- this._previousRoute = null;
131
-
132
144
  // Bind event listeners
133
145
  window.addEventListener("popstate", this._onChanged.bind(this));
134
146
  document.body.addEventListener("click", this._handleClick.bind(this));
@@ -142,6 +154,17 @@ class BreezeRouter {
142
154
  this._onChanged();
143
155
  }
144
156
 
157
+ /**
158
+ * Register the callback when the router is ready.
159
+ *
160
+ * @param {Function} fn - Callback function.
161
+ */
162
+ ready(fn) {
163
+ if (typeof fn === 'function') {
164
+ this.#isReadyCallbacks.push(fn);
165
+ }
166
+ }
167
+
145
168
  /**
146
169
  * Adds a new route to the router.
147
170
  * @param {string} route - The route path to add.
@@ -165,19 +188,22 @@ class BreezeRouter {
165
188
  const types = {};
166
189
 
167
190
  // Route has types defined.
168
- if (route.includes('<')) {
191
+ if (route.includes("<")) {
169
192
  // Split the URL segments by '/'.
170
- const urlSegments = route.split('/').filter(segment => segment.includes('<'));
193
+ const urlSegments = route
194
+ .split("/")
195
+ .filter((segment) => segment.includes("<"));
171
196
 
172
197
  // Loop through each segment and get its type.
173
- urlSegments.forEach(segment => {
198
+ urlSegments.forEach((segment) => {
174
199
  // Get the type.
175
- const type = segment.match(/\<.+\>/)?.[0]
176
- .replace('<', '')
177
- .replace('>', '');
200
+ const type = segment
201
+ .match(/\<.+\>/)?.[0]
202
+ .replace("<", "")
203
+ .replace(">", "");
178
204
 
179
205
  // Get the prop name.
180
- const propName = segment.replace(/\<.+\>/, '').replace(':', '');
206
+ const propName = segment.replace(/\<.+\>/, "").replace(":", "");
181
207
 
182
208
  // Store the prop name and type.
183
209
  types[propName] = type;
@@ -185,7 +211,7 @@ class BreezeRouter {
185
211
  }
186
212
 
187
213
  // Remove all '<type>' definitions.
188
- route = route.replaceAll(/\<.+\>/g, '');
214
+ route = route.replaceAll(/\<.+\>/g, "");
189
215
 
190
216
  this._routes[route] = {
191
217
  path: route,
@@ -202,6 +228,9 @@ class BreezeRouter {
202
228
  * @returns {void}
203
229
  */
204
230
  navigateTo(url) {
231
+ this.previousPath = this.currentPath;
232
+ this.previousRoute = this.currentRoute;
233
+ this.previousParams = this.currentParams;
205
234
  window.history.pushState({ url }, "", url);
206
235
  this._onChanged();
207
236
  }
@@ -219,14 +248,32 @@ class BreezeRouter {
219
248
  const path = window.location.pathname;
220
249
  const { route, params } = this._matchUrlToRoute(path);
221
250
 
251
+ this.currentPath = addTrailingSlash(path);
252
+ this.currentRoute = route;
253
+ this.currentParams = params;
254
+
222
255
  // If no matching route found, route will be '404' route
223
- // which has been handled by _matchUrlToRoute already
256
+ // which has been handled by _matchUrlToRoute already.
224
257
  await this._handleRoute({ route, params });
225
258
 
226
- /**
227
- * Update previous route, so application route handler can check and decide if it should re-render whole page.
228
- */
229
- this._previousRoute = route;
259
+ if (!this.#isReady) {
260
+ this.#isReady = true;
261
+ this.#isReadyCallbacks.forEach(fn => fn(this));
262
+ }
263
+
264
+ document.dispatchEvent(
265
+ new CustomEvent("breeze-route-changed", {
266
+ detail: {
267
+ previousPath: this.previousPath,
268
+ previousRoute: this.previousRoute,
269
+ previousParams: this.previousParams,
270
+ currentPath: this.currentPath,
271
+ currentRoute: this.currentRoute,
272
+ currentParams: this.currentParams,
273
+ router: this,
274
+ },
275
+ }),
276
+ );
230
277
  }
231
278
 
232
279
  /**
@@ -257,49 +304,50 @@ class BreezeRouter {
257
304
  url = addTrailingSlash(url);
258
305
  }
259
306
 
260
- const matchedRoute = Object.entries(this._routes).find(([route, routeObject]) => {
261
- if (url.split("/").length !== route.split("/").length) {
262
- return false;
263
- }
307
+ const matchedRoute = Object.entries(this._routes).find(
308
+ ([route, routeObject]) => {
309
+ if (url.split("/").length !== route.split("/").length) {
310
+ return false;
311
+ }
264
312
 
265
- const { types } = routeObject;
313
+ const { types } = routeObject;
266
314
 
267
- let routeSegments = route.split("/").slice(1);
268
- let urlSegments = url.split("/").slice(1);
315
+ let routeSegments = route.split("/").slice(1);
316
+ let urlSegments = url.split("/").slice(1);
269
317
 
270
- // If each segment in the url matches the corresponding segment in the route path,
271
- // or the route path segment starts with a ':' then the route is matched.
272
- const match = routeSegments.every((segment, i) => {
273
- if (!segment.startsWith(":")) {
274
- return segment === urlSegments[i];
275
- }
318
+ // If each segment in the url matches the corresponding segment in the route path,
319
+ // or the route path segment starts with a ':' then the route is matched.
320
+ const match = routeSegments.every((segment, i) => {
321
+ if (!segment.startsWith(":")) {
322
+ return segment === urlSegments[i];
323
+ }
276
324
 
277
- // /product/:id => segment/segment
278
- // /product/123 => urlSegment/urlSegment
279
- const type = types[segment.replace(':', '')];
325
+ // /product/:id => segment/segment
326
+ // /product/123 => urlSegment/urlSegment
327
+ const type = types[segment.replace(":", "")];
280
328
 
281
- // const segmentType =
282
- const isValid = type === 'number'
283
- ? Number.isInteger(Number(urlSegments[i]))
284
- : true;
329
+ // const segmentType =
330
+ const isValid =
331
+ type === "number" ? Number.isInteger(Number(urlSegments[i])) : true;
285
332
 
286
- return isValid;
287
- });
288
-
289
- if (!match) {
290
- return false;
291
- }
333
+ return isValid;
334
+ });
292
335
 
293
- // If the route matches the URL, pull out any params from the URL.
294
- routeSegments.forEach((segment, i) => {
295
- if (segment.startsWith(":")) {
296
- const propName = segment.slice(1);
297
- params[propName] = decodeURIComponent(urlSegments[i]);
336
+ if (!match) {
337
+ return false;
298
338
  }
299
- });
300
339
 
301
- return true;
302
- });
340
+ // If the route matches the URL, pull out any params from the URL.
341
+ routeSegments.forEach((segment, i) => {
342
+ if (segment.startsWith(":")) {
343
+ const propName = segment.slice(1);
344
+ params[propName] = decodeURIComponent(urlSegments[i]);
345
+ }
346
+ });
347
+
348
+ return true;
349
+ },
350
+ );
303
351
 
304
352
  if (matchedRoute) {
305
353
  return {
@@ -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 if (!fn) return false;\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 if (!fn) return false;\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a given 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 * Add trailing slash of a given url.\n * @param {string} url\n * @returns {string}\n */\nexport const addTrailingSlash = (url) => {\n url = url.trim();\n if (!url.endsWith(\"/\")) {\n url = url + \"/\";\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\tisFunction,\n\tisAsyncFunction,\n\tremoveTrailingSlash,\n\taddTrailingSlash,\n\tfindAnchor,\n\tshouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n\t/**\n\t * `data-` attribute to allow users to skip the router behavior,\n\t * anchor link with this attribute set will behavior as normal link.\n\t * @type {string}\n\t */\n\t#ignoreAttribute = \"data-breeze-ignore\";\n\n\t/**\n\t * Creates a new BreezeRouter instance.\n\t * @param {object} config - Config.\n\t * @constructor\n\t */\n\tconstructor(config) {\n\t\tconst { ignoreAttribute } = config || {};\n\n\t\tif (ignoreAttribute) {\n\t\t\tthis.#ignoreAttribute = ignoreAttribute;\n\t\t}\n\n\t\t/**\n\t\t * Object containing all registered routes.\n\t\t * @type {object}\n\t\t * @private\n\t\t */\n\t\tthis._routes = {};\n\n\t\t/**\n\t\t * The previous route that was navigated to\n\t\t * @type {import('./types.js').Route|null}\n\t\t * @private\n\t\t */\n\t\tthis._previousRoute = null;\n\n\t\t// Bind event listeners\n\t\twindow.addEventListener(\"popstate\", this._onChanged.bind(this));\n\t\tdocument.body.addEventListener(\"click\", this._handleClick.bind(this));\n\t}\n\n\t/**\n\t * Starts the router.\n\t * @returns {void}\n\t */\n\tstart() {\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Adds a new route to the router.\n\t * @param {string} route - The route path to add.\n\t * @param {Function} handler - The async function to handle the route\n\t * @returns {BreezeRouter|void} The BreezeRouter instance.\n\t */\n\tadd(route, handler) {\n\t\troute = route.trim();\n\t\tif (route !== \"/\") {\n\t\t\troute = addTrailingSlash(route);\n\t\t}\n\n\t\tif (this._routes[route]) {\n\t\t\treturn console.warn(`Route already exists: ${route}`);\n\t\t}\n\n\t\tif (typeof handler !== \"function\") {\n\t\t\treturn console.error(`handler of route '${route}' must be a function.`);\n\t\t}\n\n\t\tconst types = {};\n\n\t\t// Route has types defined.\n\t\tif (route.includes('<')) {\n\t\t\t// Split the URL segments by '/'.\n\t\t\tconst urlSegments = route.split('/').filter(segment => segment.includes('<'));\n\n\t\t\t// Loop through each segment and get its type.\n\t\t\turlSegments.forEach(segment => {\n\t\t\t\t// Get the type.\n\t\t\t\tconst type = segment.match(/\\<.+\\>/)?.[0]\n\t\t\t\t\t.replace('<', '')\n\t\t\t\t\t.replace('>', '');\n\n\t\t\t\t// Get the prop name.\n\t\t\t\tconst propName = segment.replace(/\\<.+\\>/, '').replace(':', '');\n\n\t\t\t\t// Store the prop name and type.\n\t\t\t\ttypes[propName] = type;\n\t\t\t});\n\t\t}\n\n\t\t// Remove all '<type>' definitions.\n\t\troute = route.replaceAll(/\\<.+\\>/g, '');\n\n\t\tthis._routes[route] = {\n\t\t\tpath: route,\n\t\t\ttypes,\n\t\t\thandler,\n\t\t};\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Navigates to the specified URL.\n\t * @param {string} url - The URL to navigate to\n\t * @returns {void}\n\t */\n\tnavigateTo(url) {\n\t\twindow.history.pushState({ url }, \"\", url);\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Redirects a URL\n\t * @param {string} url\n\t * @returns {void}\n\t */\n\tredirect(url) {\n\t\tthis.navigateTo(url);\n\t}\n\n\tasync _onChanged() {\n\t\tconst path = window.location.pathname;\n\t\tconst { route, params } = this._matchUrlToRoute(path);\n\n\t\t// If no matching route found, route will be '404' route\n\t\t// which has been handled by _matchUrlToRoute already\n\t\tawait this._handleRoute({ route, params });\n\n\t\t/**\n\t\t * Update previous route, so application route handler can check and decide if it should re-render whole page.\n\t\t */\n\t\tthis._previousRoute = route;\n\t}\n\n\t/**\n\t * Processes route callbacks registered by app\n\t * @param {import('./types.js').MatchedRoute} options\n\t * @returns {Promise<void>}\n\t */\n\tasync _handleRoute({ route, params }) {\n\t\tif (isFunction(route?.handler)) {\n\t\t\troute.handler({ route, params });\n\t\t}\n\n\t\tif (isAsyncFunction(route?.handler)) {\n\t\t\tawait route.handler({ route, params });\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {string} url - Current url users visite or nagivate to.\n\t * @returns {import('./types.js').MatchedRoute}\n\t */\n\t_matchUrlToRoute(url) {\n\t\t/** @type {import('./types.js').RouteParams} */\n\t\tconst params = {};\n\n\t\tif (url !== \"/\") {\n\t\t\turl = addTrailingSlash(url);\n\t\t}\n\n\t\tconst matchedRoute = Object.entries(this._routes).find(([route, routeObject]) => {\n\t\t\tif (url.split(\"/\").length !== route.split(\"/\").length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst { types } = routeObject;\n\n\t\t\tlet routeSegments = route.split(\"/\").slice(1);\n\t\t\tlet urlSegments = url.split(\"/\").slice(1);\n\n\t\t\t// If each segment in the url matches the corresponding segment in the route path,\n\t\t\t// or the route path segment starts with a ':' then the route is matched.\n\t\t\tconst match = routeSegments.every((segment, i) => {\n\t\t\t\tif (!segment.startsWith(\":\")) {\n\t\t\t\t\treturn segment === urlSegments[i];\n\t\t\t\t}\n\n\t\t\t\t// /product/:id => segment/segment\n\t\t\t\t// /product/123 => urlSegment/urlSegment\n\t\t\t\tconst type = types[segment.replace(':', '')];\n\n\t\t\t\t// const segmentType = \n\t\t\t\tconst isValid = type === 'number'\n\t\t\t\t\t? Number.isInteger(Number(urlSegments[i]))\n\t\t\t\t\t: true;\n\n\t\t\t\treturn isValid;\n\t\t\t});\n\n\t\t\tif (!match) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// If the route matches the URL, pull out any params from the URL.\n\t\t\trouteSegments.forEach((segment, i) => {\n\t\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\t\tconst propName = segment.slice(1);\n\t\t\t\t\tparams[propName] = decodeURIComponent(urlSegments[i]);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn true;\n\t\t});\n\n\t\tif (matchedRoute) {\n\t\t\treturn {\n\t\t\t\troute: this._routes[matchedRoute[0]],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: new URLSearchParams(window.location.search),\n\t\t\t};\n\t\t} else {\n\t\t\treturn {\n\t\t\t\troute: this._routes[404],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: null,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Handles <a> link clicks\n\t * @param {Event} event\n\t * @returns {void}\n\t */\n\t_handleClick(event) {\n\t\tconst anchor = findAnchor(event);\n\t\tif (!anchor) {\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * Allow users to define an `data-` attribute to skip the route handling.\n\t\t * Default to `data-breeze-ignore`.\n\t\t */\n\t\tif (anchor.hasAttribute(this.#ignoreAttribute)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!shouldRouterHandleClick(event, anchor)) {\n\t\t\treturn;\n\t\t}\n\n\t\tevent.preventDefault();\n\t\tlet href = anchor.getAttribute(\"href\")?.trim();\n\t\tif (!href?.startsWith(\"/\")) {\n\t\t\thref = \"/\" + href;\n\t\t}\n\n\t\tthis.navigateTo(href);\n\t}\n\n\t/**\n\t * Add or remove search param to current url.\n\t * @param {HTMLInputElement} checkbox\n\t * @param {string} value\n\t * @returns void\n\t */\n\ttoggleParam(checkbox, value) {\n\t\tconst params = new URLSearchParams(location.search);\n\t\tconst name = checkbox.getAttribute(\"name\");\n\t\tif (!name) {\n\t\t\treturn console.warn(`name attribute is not set on ${checkbox.outerHTML}`);\n\t\t}\n\t\tif (checkbox.checked) {\n\t\t\t!params.has(name) && params.set(name, value);\n\t\t} else if (!checkbox.checked) {\n\t\t\tparams.has(name) && params.delete(name);\n\t\t}\n\n\t\tconst newUrl = !!params.size\n\t\t\t? `${location.pathname}?${params.toString()}`\n\t\t\t: location.pathname;\n\t\tthis.navigateTo(newUrl);\n\t}\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 * Check if given param is function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isFunction = (fn) => {\n if (!fn) return false;\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 if (!fn) return false;\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a given 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 * Add trailing slash of a given url.\n * @param {string} url\n * @returns {string}\n */\nexport const addTrailingSlash = (url) => {\n url = url.trim();\n if (!url.endsWith(\"/\")) {\n url = url + \"/\";\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\tisFunction,\n\tisAsyncFunction,\n\tremoveTrailingSlash,\n\taddTrailingSlash,\n\tfindAnchor,\n\tshouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n\t/**\n\t * `data-` attribute to allow users to skip the router behavior,\n\t * anchor link with this attribute set will behavior as normal link.\n\t * @type {string}\n\t */\n\t#ignoreAttribute = \"data-breeze-ignore\";\n\t#isReady = false;\n\t#isReadyCallbacks = [];\n\n\tpreviousPath = \"\";\n\t/**\n\t * The previous route that was navigated to.\n\t *\n\t * @type {import('./types.js').Route|null}\n\t */\n\tpreviousRoute = null;\n\tpreviousParams = {};\n\n\tcurrentPath = \"\";\n\t/**\n\t * The current route that we're on.\n\t *\n\t * @type {import('./types.js').Route|null}\n\t */\n\tcurrentRoute = null;\n\tcurrentParams = {};\n\n\t/**\n\t * Creates a new BreezeRouter instance.\n\t * @param {object} config - Config.\n\t * @constructor\n\t */\n\tconstructor(config) {\n\t\tconst { ignoreAttribute } = config || {};\n\n\t\tif (ignoreAttribute) {\n\t\t\tthis.#ignoreAttribute = ignoreAttribute;\n\t\t}\n\n\t\t/**\n\t\t * Object containing all registered routes.\n\t\t * @type {object}\n\t\t * @private\n\t\t */\n\t\tthis._routes = {};\n\n\t\t// Bind event listeners\n\t\twindow.addEventListener(\"popstate\", this._onChanged.bind(this));\n\t\tdocument.body.addEventListener(\"click\", this._handleClick.bind(this));\n\t}\n\n\t/**\n\t * Starts the router.\n\t * @returns {void}\n\t */\n\tstart() {\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Register the callback when the router is ready.\n\t * \n\t * @param {Function} fn - Callback function.\n\t */\n\tready(fn) {\n\t\tif (typeof fn === 'function') {\n\t\t\tthis.#isReadyCallbacks.push(fn);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a new route to the router.\n\t * @param {string} route - The route path to add.\n\t * @param {Function} handler - The async function to handle the route\n\t * @returns {BreezeRouter|void} The BreezeRouter instance.\n\t */\n\tadd(route, handler) {\n\t\troute = route.trim();\n\t\tif (route !== \"/\") {\n\t\t\troute = addTrailingSlash(route);\n\t\t}\n\n\t\tif (this._routes[route]) {\n\t\t\treturn console.warn(`Route already exists: ${route}`);\n\t\t}\n\n\t\tif (typeof handler !== \"function\") {\n\t\t\treturn console.error(`handler of route '${route}' must be a function.`);\n\t\t}\n\n\t\tconst types = {};\n\n\t\t// Route has types defined.\n\t\tif (route.includes(\"<\")) {\n\t\t\t// Split the URL segments by '/'.\n\t\t\tconst urlSegments = route\n\t\t\t\t.split(\"/\")\n\t\t\t\t.filter((segment) => segment.includes(\"<\"));\n\n\t\t\t// Loop through each segment and get its type.\n\t\t\turlSegments.forEach((segment) => {\n\t\t\t\t// Get the type.\n\t\t\t\tconst type = segment\n\t\t\t\t\t.match(/\\<.+\\>/)?.[0]\n\t\t\t\t\t.replace(\"<\", \"\")\n\t\t\t\t\t.replace(\">\", \"\");\n\n\t\t\t\t// Get the prop name.\n\t\t\t\tconst propName = segment.replace(/\\<.+\\>/, \"\").replace(\":\", \"\");\n\n\t\t\t\t// Store the prop name and type.\n\t\t\t\ttypes[propName] = type;\n\t\t\t});\n\t\t}\n\n\t\t// Remove all '<type>' definitions.\n\t\troute = route.replaceAll(/\\<.+\\>/g, \"\");\n\n\t\tthis._routes[route] = {\n\t\t\tpath: route,\n\t\t\ttypes,\n\t\t\thandler,\n\t\t};\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Navigates to the specified URL.\n\t * @param {string} url - The URL to navigate to\n\t * @returns {void}\n\t */\n\tnavigateTo(url) {\n\t\tthis.previousPath = this.currentPath;\n\t\tthis.previousRoute = this.currentRoute;\n\t\tthis.previousParams = this.currentParams;\n\t\twindow.history.pushState({ url }, \"\", url);\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Redirects a URL\n\t * @param {string} url\n\t * @returns {void}\n\t */\n\tredirect(url) {\n\t\tthis.navigateTo(url);\n\t}\n\n\tasync _onChanged() {\n\t\tconst path = window.location.pathname;\n\t\tconst { route, params } = this._matchUrlToRoute(path);\n\n\t\tthis.currentPath = addTrailingSlash(path);\n\t\tthis.currentRoute = route;\n\t\tthis.currentParams = params;\n\n\t\t// If no matching route found, route will be '404' route\n\t\t// which has been handled by _matchUrlToRoute already.\n\t\tawait this._handleRoute({ route, params });\n\n\t\tif (!this.#isReady) {\n\t\t\tthis.#isReady = true;\n\t\t\tthis.#isReadyCallbacks.forEach(fn => fn(this));\n\t\t}\n\n\t\tdocument.dispatchEvent(\n\t\t\tnew CustomEvent(\"breeze-route-changed\", {\n\t\t\t\tdetail: {\n\t\t\t\t\tpreviousPath: this.previousPath,\n\t\t\t\t\tpreviousRoute: this.previousRoute,\n\t\t\t\t\tpreviousParams: this.previousParams,\n\t\t\t\t\tcurrentPath: this.currentPath,\n\t\t\t\t\tcurrentRoute: this.currentRoute,\n\t\t\t\t\tcurrentParams: this.currentParams,\n\t\t\t\t\trouter: this,\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Processes route callbacks registered by app\n\t * @param {import('./types.js').MatchedRoute} options\n\t * @returns {Promise<void>}\n\t */\n\tasync _handleRoute({ route, params }) {\n\t\tif (isFunction(route?.handler)) {\n\t\t\troute.handler({ route, params });\n\t\t}\n\n\t\tif (isAsyncFunction(route?.handler)) {\n\t\t\tawait route.handler({ route, params });\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {string} url - Current url users visite or nagivate to.\n\t * @returns {import('./types.js').MatchedRoute}\n\t */\n\t_matchUrlToRoute(url) {\n\t\t/** @type {import('./types.js').RouteParams} */\n\t\tconst params = {};\n\n\t\tif (url !== \"/\") {\n\t\t\turl = addTrailingSlash(url);\n\t\t}\n\n\t\tconst matchedRoute = Object.entries(this._routes).find(\n\t\t\t([route, routeObject]) => {\n\t\t\t\tif (url.split(\"/\").length !== route.split(\"/\").length) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst { types } = routeObject;\n\n\t\t\t\tlet routeSegments = route.split(\"/\").slice(1);\n\t\t\t\tlet urlSegments = url.split(\"/\").slice(1);\n\n\t\t\t\t// If each segment in the url matches the corresponding segment in the route path,\n\t\t\t\t// or the route path segment starts with a ':' then the route is matched.\n\t\t\t\tconst match = routeSegments.every((segment, i) => {\n\t\t\t\t\tif (!segment.startsWith(\":\")) {\n\t\t\t\t\t\treturn segment === urlSegments[i];\n\t\t\t\t\t}\n\n\t\t\t\t\t// /product/:id => segment/segment\n\t\t\t\t\t// /product/123 => urlSegment/urlSegment\n\t\t\t\t\tconst type = types[segment.replace(\":\", \"\")];\n\n\t\t\t\t\t// const segmentType =\n\t\t\t\t\tconst isValid =\n\t\t\t\t\t\ttype === \"number\" ? Number.isInteger(Number(urlSegments[i])) : true;\n\n\t\t\t\t\treturn isValid;\n\t\t\t\t});\n\n\t\t\t\tif (!match) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// If the route matches the URL, pull out any params from the URL.\n\t\t\t\trouteSegments.forEach((segment, i) => {\n\t\t\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\t\t\tconst propName = segment.slice(1);\n\t\t\t\t\t\tparams[propName] = decodeURIComponent(urlSegments[i]);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t);\n\n\t\tif (matchedRoute) {\n\t\t\treturn {\n\t\t\t\troute: this._routes[matchedRoute[0]],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: new URLSearchParams(window.location.search),\n\t\t\t};\n\t\t} else {\n\t\t\treturn {\n\t\t\t\troute: this._routes[404],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: null,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Handles <a> link clicks\n\t * @param {Event} event\n\t * @returns {void}\n\t */\n\t_handleClick(event) {\n\t\tconst anchor = findAnchor(event);\n\t\tif (!anchor) {\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * Allow users to define an `data-` attribute to skip the route handling.\n\t\t * Default to `data-breeze-ignore`.\n\t\t */\n\t\tif (anchor.hasAttribute(this.#ignoreAttribute)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!shouldRouterHandleClick(event, anchor)) {\n\t\t\treturn;\n\t\t}\n\n\t\tevent.preventDefault();\n\t\tlet href = anchor.getAttribute(\"href\")?.trim();\n\t\tif (!href?.startsWith(\"/\")) {\n\t\t\thref = \"/\" + href;\n\t\t}\n\n\t\tthis.navigateTo(href);\n\t}\n\n\t/**\n\t * Add or remove search param to current url.\n\t * @param {HTMLInputElement} checkbox\n\t * @param {string} value\n\t * @returns void\n\t */\n\ttoggleParam(checkbox, value) {\n\t\tconst params = new URLSearchParams(location.search);\n\t\tconst name = checkbox.getAttribute(\"name\");\n\t\tif (!name) {\n\t\t\treturn console.warn(`name attribute is not set on ${checkbox.outerHTML}`);\n\t\t}\n\t\tif (checkbox.checked) {\n\t\t\t!params.has(name) && params.set(name, value);\n\t\t} else if (!checkbox.checked) {\n\t\t\tparams.has(name) && params.delete(name);\n\t\t}\n\n\t\tconst newUrl = !!params.size\n\t\t\t? `${location.pathname}?${params.toString()}`\n\t\t\t: location.pathname;\n\t\tthis.navigateTo(newUrl);\n\t}\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const t=t=>((t=t.trim()).endsWith("/")||(t+="/"),t);class e{#t="data-breeze-ignore";constructor(t){const{ignoreAttribute:e}=t||{};e&&(this.#t=e),this._routes={},this._previousRoute=null,window.addEventListener("popstate",this._onChanged.bind(this)),document.body.addEventListener("click",this._handleClick.bind(this))}start(){this._onChanged()}add(e,r){if("/"!==(e=e.trim())&&(e=t(e)),this._routes[e])return console.warn(`Route already exists: ${e}`);if("function"!=typeof r)return console.error(`handler of route '${e}' must be a function.`);const a={};if(e.includes("<")){e.split("/").filter((t=>t.includes("<"))).forEach((t=>{const e=t.match(/\<.+\>/)?.[0].replace("<","").replace(">",""),r=t.replace(/\<.+\>/,"").replace(":","");a[r]=e}))}return e=e.replaceAll(/\<.+\>/g,""),this._routes[e]={path:e,types:a,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}){var r;r=t?.handler,r&&"function"===r.constructor.name.toLowerCase()&&t.handler({route:t,params:e}),(t=>!!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.entries(this._routes).find((([t,a])=>{if(e.split("/").length!==t.split("/").length)return!1;const{types:n}=a;let s=t.split("/").slice(1),i=e.split("/").slice(1);return!!s.every(((t,e)=>{if(!t.startsWith(":"))return t===i[e];return"number"!==n[t.replace(":","")]||Number.isInteger(Number(i[e]))}))&&(s.forEach(((t,e)=>{if(t.startsWith(":")){const a=t.slice(1);r[a]=decodeURIComponent(i[e])}})),!0)}));return a?{route:this._routes[a[0]],params:r,searchParams:new URLSearchParams(window.location.search)}:{route:this._routes[404],params:r,searchParams:null}}_handleClick(t){const e=t.composedPath().find((t=>"A"===t.tagName));if(!e)return;if(e.hasAttribute(this.#t))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};
1
+ const t=t=>((t=t.trim()).endsWith("/")||(t+="/"),t);class e{#t="data-breeze-ignore";#e=!1;#r=[];previousPath="";previousRoute=null;previousParams={};currentPath="";currentRoute=null;currentParams={};constructor(t){const{ignoreAttribute:e}=t||{};e&&(this.#t=e),this._routes={},window.addEventListener("popstate",this._onChanged.bind(this)),document.body.addEventListener("click",this._handleClick.bind(this))}start(){this._onChanged()}ready(t){"function"==typeof t&&this.#r.push(t)}add(e,r){if("/"!==(e=e.trim())&&(e=t(e)),this._routes[e])return console.warn(`Route already exists: ${e}`);if("function"!=typeof r)return console.error(`handler of route '${e}' must be a function.`);const a={};if(e.includes("<")){e.split("/").filter((t=>t.includes("<"))).forEach((t=>{const e=t.match(/\<.+\>/)?.[0].replace("<","").replace(">",""),r=t.replace(/\<.+\>/,"").replace(":","");a[r]=e}))}return e=e.replaceAll(/\<.+\>/g,""),this._routes[e]={path:e,types:a,handler:r},this}navigateTo(t){this.previousPath=this.currentPath,this.previousRoute=this.currentRoute,this.previousParams=this.currentParams,window.history.pushState({url:t},"",t),this._onChanged()}redirect(t){this.navigateTo(t)}async _onChanged(){const e=window.location.pathname,{route:r,params:a}=this._matchUrlToRoute(e);this.currentPath=t(e),this.currentRoute=r,this.currentParams=a,await this._handleRoute({route:r,params:a}),this.#e||(this.#e=!0,this.#r.forEach((t=>t(this)))),document.dispatchEvent(new CustomEvent("breeze-route-changed",{detail:{previousPath:this.previousPath,previousRoute:this.previousRoute,previousParams:this.previousParams,currentPath:this.currentPath,currentRoute:this.currentRoute,currentParams:this.currentParams,router:this}}))}async _handleRoute({route:t,params:e}){var r;r=t?.handler,r&&"function"===r.constructor.name.toLowerCase()&&t.handler({route:t,params:e}),(t=>!!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.entries(this._routes).find((([t,a])=>{if(e.split("/").length!==t.split("/").length)return!1;const{types:s}=a;let i=t.split("/").slice(1),n=e.split("/").slice(1);return!!i.every(((t,e)=>{if(!t.startsWith(":"))return t===n[e];return"number"!==s[t.replace(":","")]||Number.isInteger(Number(n[e]))}))&&(i.forEach(((t,e)=>{if(t.startsWith(":")){const a=t.slice(1);r[a]=decodeURIComponent(n[e])}})),!0)}));return a?{route:this._routes[a[0]],params:r,searchParams:new URLSearchParams(window.location.search)}:{route:this._routes[404],params:r,searchParams:null}}_handleClick(t){const e=t.composedPath().find((t=>"A"===t.tagName));if(!e)return;if(e.hasAttribute(this.#t))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 s=r.size?`${location.pathname}?${r.toString()}`:location.pathname;this.navigateTo(s)}}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 if (!fn) return false;\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 if (!fn) return false;\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a given 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 * Add trailing slash of a given url.\n * @param {string} url\n * @returns {string}\n */\nexport const addTrailingSlash = (url) => {\n url = url.trim();\n if (!url.endsWith(\"/\")) {\n url = url + \"/\";\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\tisFunction,\n\tisAsyncFunction,\n\tremoveTrailingSlash,\n\taddTrailingSlash,\n\tfindAnchor,\n\tshouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n\t/**\n\t * `data-` attribute to allow users to skip the router behavior,\n\t * anchor link with this attribute set will behavior as normal link.\n\t * @type {string}\n\t */\n\t#ignoreAttribute = \"data-breeze-ignore\";\n\n\t/**\n\t * Creates a new BreezeRouter instance.\n\t * @param {object} config - Config.\n\t * @constructor\n\t */\n\tconstructor(config) {\n\t\tconst { ignoreAttribute } = config || {};\n\n\t\tif (ignoreAttribute) {\n\t\t\tthis.#ignoreAttribute = ignoreAttribute;\n\t\t}\n\n\t\t/**\n\t\t * Object containing all registered routes.\n\t\t * @type {object}\n\t\t * @private\n\t\t */\n\t\tthis._routes = {};\n\n\t\t/**\n\t\t * The previous route that was navigated to\n\t\t * @type {import('./types.js').Route|null}\n\t\t * @private\n\t\t */\n\t\tthis._previousRoute = null;\n\n\t\t// Bind event listeners\n\t\twindow.addEventListener(\"popstate\", this._onChanged.bind(this));\n\t\tdocument.body.addEventListener(\"click\", this._handleClick.bind(this));\n\t}\n\n\t/**\n\t * Starts the router.\n\t * @returns {void}\n\t */\n\tstart() {\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Adds a new route to the router.\n\t * @param {string} route - The route path to add.\n\t * @param {Function} handler - The async function to handle the route\n\t * @returns {BreezeRouter|void} The BreezeRouter instance.\n\t */\n\tadd(route, handler) {\n\t\troute = route.trim();\n\t\tif (route !== \"/\") {\n\t\t\troute = addTrailingSlash(route);\n\t\t}\n\n\t\tif (this._routes[route]) {\n\t\t\treturn console.warn(`Route already exists: ${route}`);\n\t\t}\n\n\t\tif (typeof handler !== \"function\") {\n\t\t\treturn console.error(`handler of route '${route}' must be a function.`);\n\t\t}\n\n\t\tconst types = {};\n\n\t\t// Route has types defined.\n\t\tif (route.includes('<')) {\n\t\t\t// Split the URL segments by '/'.\n\t\t\tconst urlSegments = route.split('/').filter(segment => segment.includes('<'));\n\n\t\t\t// Loop through each segment and get its type.\n\t\t\turlSegments.forEach(segment => {\n\t\t\t\t// Get the type.\n\t\t\t\tconst type = segment.match(/\\<.+\\>/)?.[0]\n\t\t\t\t\t.replace('<', '')\n\t\t\t\t\t.replace('>', '');\n\n\t\t\t\t// Get the prop name.\n\t\t\t\tconst propName = segment.replace(/\\<.+\\>/, '').replace(':', '');\n\n\t\t\t\t// Store the prop name and type.\n\t\t\t\ttypes[propName] = type;\n\t\t\t});\n\t\t}\n\n\t\t// Remove all '<type>' definitions.\n\t\troute = route.replaceAll(/\\<.+\\>/g, '');\n\n\t\tthis._routes[route] = {\n\t\t\tpath: route,\n\t\t\ttypes,\n\t\t\thandler,\n\t\t};\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Navigates to the specified URL.\n\t * @param {string} url - The URL to navigate to\n\t * @returns {void}\n\t */\n\tnavigateTo(url) {\n\t\twindow.history.pushState({ url }, \"\", url);\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Redirects a URL\n\t * @param {string} url\n\t * @returns {void}\n\t */\n\tredirect(url) {\n\t\tthis.navigateTo(url);\n\t}\n\n\tasync _onChanged() {\n\t\tconst path = window.location.pathname;\n\t\tconst { route, params } = this._matchUrlToRoute(path);\n\n\t\t// If no matching route found, route will be '404' route\n\t\t// which has been handled by _matchUrlToRoute already\n\t\tawait this._handleRoute({ route, params });\n\n\t\t/**\n\t\t * Update previous route, so application route handler can check and decide if it should re-render whole page.\n\t\t */\n\t\tthis._previousRoute = route;\n\t}\n\n\t/**\n\t * Processes route callbacks registered by app\n\t * @param {import('./types.js').MatchedRoute} options\n\t * @returns {Promise<void>}\n\t */\n\tasync _handleRoute({ route, params }) {\n\t\tif (isFunction(route?.handler)) {\n\t\t\troute.handler({ route, params });\n\t\t}\n\n\t\tif (isAsyncFunction(route?.handler)) {\n\t\t\tawait route.handler({ route, params });\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {string} url - Current url users visite or nagivate to.\n\t * @returns {import('./types.js').MatchedRoute}\n\t */\n\t_matchUrlToRoute(url) {\n\t\t/** @type {import('./types.js').RouteParams} */\n\t\tconst params = {};\n\n\t\tif (url !== \"/\") {\n\t\t\turl = addTrailingSlash(url);\n\t\t}\n\n\t\tconst matchedRoute = Object.entries(this._routes).find(([route, routeObject]) => {\n\t\t\tif (url.split(\"/\").length !== route.split(\"/\").length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst { types } = routeObject;\n\n\t\t\tlet routeSegments = route.split(\"/\").slice(1);\n\t\t\tlet urlSegments = url.split(\"/\").slice(1);\n\n\t\t\t// If each segment in the url matches the corresponding segment in the route path,\n\t\t\t// or the route path segment starts with a ':' then the route is matched.\n\t\t\tconst match = routeSegments.every((segment, i) => {\n\t\t\t\tif (!segment.startsWith(\":\")) {\n\t\t\t\t\treturn segment === urlSegments[i];\n\t\t\t\t}\n\n\t\t\t\t// /product/:id => segment/segment\n\t\t\t\t// /product/123 => urlSegment/urlSegment\n\t\t\t\tconst type = types[segment.replace(':', '')];\n\n\t\t\t\t// const segmentType = \n\t\t\t\tconst isValid = type === 'number'\n\t\t\t\t\t? Number.isInteger(Number(urlSegments[i]))\n\t\t\t\t\t: true;\n\n\t\t\t\treturn isValid;\n\t\t\t});\n\n\t\t\tif (!match) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// If the route matches the URL, pull out any params from the URL.\n\t\t\trouteSegments.forEach((segment, i) => {\n\t\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\t\tconst propName = segment.slice(1);\n\t\t\t\t\tparams[propName] = decodeURIComponent(urlSegments[i]);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn true;\n\t\t});\n\n\t\tif (matchedRoute) {\n\t\t\treturn {\n\t\t\t\troute: this._routes[matchedRoute[0]],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: new URLSearchParams(window.location.search),\n\t\t\t};\n\t\t} else {\n\t\t\treturn {\n\t\t\t\troute: this._routes[404],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: null,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Handles <a> link clicks\n\t * @param {Event} event\n\t * @returns {void}\n\t */\n\t_handleClick(event) {\n\t\tconst anchor = findAnchor(event);\n\t\tif (!anchor) {\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * Allow users to define an `data-` attribute to skip the route handling.\n\t\t * Default to `data-breeze-ignore`.\n\t\t */\n\t\tif (anchor.hasAttribute(this.#ignoreAttribute)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!shouldRouterHandleClick(event, anchor)) {\n\t\t\treturn;\n\t\t}\n\n\t\tevent.preventDefault();\n\t\tlet href = anchor.getAttribute(\"href\")?.trim();\n\t\tif (!href?.startsWith(\"/\")) {\n\t\t\thref = \"/\" + href;\n\t\t}\n\n\t\tthis.navigateTo(href);\n\t}\n\n\t/**\n\t * Add or remove search param to current url.\n\t * @param {HTMLInputElement} checkbox\n\t * @param {string} value\n\t * @returns void\n\t */\n\ttoggleParam(checkbox, value) {\n\t\tconst params = new URLSearchParams(location.search);\n\t\tconst name = checkbox.getAttribute(\"name\");\n\t\tif (!name) {\n\t\t\treturn console.warn(`name attribute is not set on ${checkbox.outerHTML}`);\n\t\t}\n\t\tif (checkbox.checked) {\n\t\t\t!params.has(name) && params.set(name, value);\n\t\t} else if (!checkbox.checked) {\n\t\t\tparams.has(name) && params.delete(name);\n\t\t}\n\n\t\tconst newUrl = !!params.size\n\t\t\t? `${location.pathname}?${params.toString()}`\n\t\t\t: location.pathname;\n\t\tthis.navigateTo(newUrl);\n\t}\n}\n"],"names":["addTrailingSlash","url","trim","endsWith","BreezeRouter","ignoreAttribute","constructor","config","this","_routes","_previousRoute","window","addEventListener","_onChanged","bind","document","body","_handleClick","start","add","route","handler","console","warn","error","types","includes","split","filter","segment","forEach","type","match","replace","propName","replaceAll","path","navigateTo","history","pushState","redirect","async","location","pathname","params","_matchUrlToRoute","_handleRoute","fn","name","toLowerCase","isAsyncFunction","matchedRoute","Object","entries","find","routeObject","length","routeSegments","slice","urlSegments","every","i","startsWith","Number","isInteger","decodeURIComponent","searchParams","URLSearchParams","search","event","anchor","composedPath","elem","tagName","hasAttribute","e","defaultPrevented","metaKey","ctrlKey","shiftKey","target","getAttribute","href","origin","shouldRouterHandleClick","preventDefault","toggleParam","checkbox","value","outerHTML","checked","has","set","delete","newUrl","size","toString"],"mappings":"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"}
1
+ {"version":3,"file":"BreezeRouter.min.js","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["// @ts-check\n/**\n * Check if given param is function\n * @param {Function} fn\n * @returns {boolean}\n */\nexport const isFunction = (fn) => {\n if (!fn) return false;\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 if (!fn) return false;\n return fn.constructor.name.toLowerCase() === \"asyncfunction\";\n};\n\n/**\n * Remove trailing slash of a given 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 * Add trailing slash of a given url.\n * @param {string} url\n * @returns {string}\n */\nexport const addTrailingSlash = (url) => {\n url = url.trim();\n if (!url.endsWith(\"/\")) {\n url = url + \"/\";\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\tisFunction,\n\tisAsyncFunction,\n\tremoveTrailingSlash,\n\taddTrailingSlash,\n\tfindAnchor,\n\tshouldRouterHandleClick,\n} from \"./utils.js\";\n\n/**\n * Class representing a router.\n */\nexport default class BreezeRouter {\n\t/**\n\t * `data-` attribute to allow users to skip the router behavior,\n\t * anchor link with this attribute set will behavior as normal link.\n\t * @type {string}\n\t */\n\t#ignoreAttribute = \"data-breeze-ignore\";\n\t#isReady = false;\n\t#isReadyCallbacks = [];\n\n\tpreviousPath = \"\";\n\t/**\n\t * The previous route that was navigated to.\n\t *\n\t * @type {import('./types.js').Route|null}\n\t */\n\tpreviousRoute = null;\n\tpreviousParams = {};\n\n\tcurrentPath = \"\";\n\t/**\n\t * The current route that we're on.\n\t *\n\t * @type {import('./types.js').Route|null}\n\t */\n\tcurrentRoute = null;\n\tcurrentParams = {};\n\n\t/**\n\t * Creates a new BreezeRouter instance.\n\t * @param {object} config - Config.\n\t * @constructor\n\t */\n\tconstructor(config) {\n\t\tconst { ignoreAttribute } = config || {};\n\n\t\tif (ignoreAttribute) {\n\t\t\tthis.#ignoreAttribute = ignoreAttribute;\n\t\t}\n\n\t\t/**\n\t\t * Object containing all registered routes.\n\t\t * @type {object}\n\t\t * @private\n\t\t */\n\t\tthis._routes = {};\n\n\t\t// Bind event listeners\n\t\twindow.addEventListener(\"popstate\", this._onChanged.bind(this));\n\t\tdocument.body.addEventListener(\"click\", this._handleClick.bind(this));\n\t}\n\n\t/**\n\t * Starts the router.\n\t * @returns {void}\n\t */\n\tstart() {\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Register the callback when the router is ready.\n\t * \n\t * @param {Function} fn - Callback function.\n\t */\n\tready(fn) {\n\t\tif (typeof fn === 'function') {\n\t\t\tthis.#isReadyCallbacks.push(fn);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a new route to the router.\n\t * @param {string} route - The route path to add.\n\t * @param {Function} handler - The async function to handle the route\n\t * @returns {BreezeRouter|void} The BreezeRouter instance.\n\t */\n\tadd(route, handler) {\n\t\troute = route.trim();\n\t\tif (route !== \"/\") {\n\t\t\troute = addTrailingSlash(route);\n\t\t}\n\n\t\tif (this._routes[route]) {\n\t\t\treturn console.warn(`Route already exists: ${route}`);\n\t\t}\n\n\t\tif (typeof handler !== \"function\") {\n\t\t\treturn console.error(`handler of route '${route}' must be a function.`);\n\t\t}\n\n\t\tconst types = {};\n\n\t\t// Route has types defined.\n\t\tif (route.includes(\"<\")) {\n\t\t\t// Split the URL segments by '/'.\n\t\t\tconst urlSegments = route\n\t\t\t\t.split(\"/\")\n\t\t\t\t.filter((segment) => segment.includes(\"<\"));\n\n\t\t\t// Loop through each segment and get its type.\n\t\t\turlSegments.forEach((segment) => {\n\t\t\t\t// Get the type.\n\t\t\t\tconst type = segment\n\t\t\t\t\t.match(/\\<.+\\>/)?.[0]\n\t\t\t\t\t.replace(\"<\", \"\")\n\t\t\t\t\t.replace(\">\", \"\");\n\n\t\t\t\t// Get the prop name.\n\t\t\t\tconst propName = segment.replace(/\\<.+\\>/, \"\").replace(\":\", \"\");\n\n\t\t\t\t// Store the prop name and type.\n\t\t\t\ttypes[propName] = type;\n\t\t\t});\n\t\t}\n\n\t\t// Remove all '<type>' definitions.\n\t\troute = route.replaceAll(/\\<.+\\>/g, \"\");\n\n\t\tthis._routes[route] = {\n\t\t\tpath: route,\n\t\t\ttypes,\n\t\t\thandler,\n\t\t};\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Navigates to the specified URL.\n\t * @param {string} url - The URL to navigate to\n\t * @returns {void}\n\t */\n\tnavigateTo(url) {\n\t\tthis.previousPath = this.currentPath;\n\t\tthis.previousRoute = this.currentRoute;\n\t\tthis.previousParams = this.currentParams;\n\t\twindow.history.pushState({ url }, \"\", url);\n\t\tthis._onChanged();\n\t}\n\n\t/**\n\t * Redirects a URL\n\t * @param {string} url\n\t * @returns {void}\n\t */\n\tredirect(url) {\n\t\tthis.navigateTo(url);\n\t}\n\n\tasync _onChanged() {\n\t\tconst path = window.location.pathname;\n\t\tconst { route, params } = this._matchUrlToRoute(path);\n\n\t\tthis.currentPath = addTrailingSlash(path);\n\t\tthis.currentRoute = route;\n\t\tthis.currentParams = params;\n\n\t\t// If no matching route found, route will be '404' route\n\t\t// which has been handled by _matchUrlToRoute already.\n\t\tawait this._handleRoute({ route, params });\n\n\t\tif (!this.#isReady) {\n\t\t\tthis.#isReady = true;\n\t\t\tthis.#isReadyCallbacks.forEach(fn => fn(this));\n\t\t}\n\n\t\tdocument.dispatchEvent(\n\t\t\tnew CustomEvent(\"breeze-route-changed\", {\n\t\t\t\tdetail: {\n\t\t\t\t\tpreviousPath: this.previousPath,\n\t\t\t\t\tpreviousRoute: this.previousRoute,\n\t\t\t\t\tpreviousParams: this.previousParams,\n\t\t\t\t\tcurrentPath: this.currentPath,\n\t\t\t\t\tcurrentRoute: this.currentRoute,\n\t\t\t\t\tcurrentParams: this.currentParams,\n\t\t\t\t\trouter: this,\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Processes route callbacks registered by app\n\t * @param {import('./types.js').MatchedRoute} options\n\t * @returns {Promise<void>}\n\t */\n\tasync _handleRoute({ route, params }) {\n\t\tif (isFunction(route?.handler)) {\n\t\t\troute.handler({ route, params });\n\t\t}\n\n\t\tif (isAsyncFunction(route?.handler)) {\n\t\t\tawait route.handler({ route, params });\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {string} url - Current url users visite or nagivate to.\n\t * @returns {import('./types.js').MatchedRoute}\n\t */\n\t_matchUrlToRoute(url) {\n\t\t/** @type {import('./types.js').RouteParams} */\n\t\tconst params = {};\n\n\t\tif (url !== \"/\") {\n\t\t\turl = addTrailingSlash(url);\n\t\t}\n\n\t\tconst matchedRoute = Object.entries(this._routes).find(\n\t\t\t([route, routeObject]) => {\n\t\t\t\tif (url.split(\"/\").length !== route.split(\"/\").length) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst { types } = routeObject;\n\n\t\t\t\tlet routeSegments = route.split(\"/\").slice(1);\n\t\t\t\tlet urlSegments = url.split(\"/\").slice(1);\n\n\t\t\t\t// If each segment in the url matches the corresponding segment in the route path,\n\t\t\t\t// or the route path segment starts with a ':' then the route is matched.\n\t\t\t\tconst match = routeSegments.every((segment, i) => {\n\t\t\t\t\tif (!segment.startsWith(\":\")) {\n\t\t\t\t\t\treturn segment === urlSegments[i];\n\t\t\t\t\t}\n\n\t\t\t\t\t// /product/:id => segment/segment\n\t\t\t\t\t// /product/123 => urlSegment/urlSegment\n\t\t\t\t\tconst type = types[segment.replace(\":\", \"\")];\n\n\t\t\t\t\t// const segmentType =\n\t\t\t\t\tconst isValid =\n\t\t\t\t\t\ttype === \"number\" ? Number.isInteger(Number(urlSegments[i])) : true;\n\n\t\t\t\t\treturn isValid;\n\t\t\t\t});\n\n\t\t\t\tif (!match) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// If the route matches the URL, pull out any params from the URL.\n\t\t\t\trouteSegments.forEach((segment, i) => {\n\t\t\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\t\t\tconst propName = segment.slice(1);\n\t\t\t\t\t\tparams[propName] = decodeURIComponent(urlSegments[i]);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t);\n\n\t\tif (matchedRoute) {\n\t\t\treturn {\n\t\t\t\troute: this._routes[matchedRoute[0]],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: new URLSearchParams(window.location.search),\n\t\t\t};\n\t\t} else {\n\t\t\treturn {\n\t\t\t\troute: this._routes[404],\n\t\t\t\tparams,\n\t\t\t\tsearchParams: null,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Handles <a> link clicks\n\t * @param {Event} event\n\t * @returns {void}\n\t */\n\t_handleClick(event) {\n\t\tconst anchor = findAnchor(event);\n\t\tif (!anchor) {\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * Allow users to define an `data-` attribute to skip the route handling.\n\t\t * Default to `data-breeze-ignore`.\n\t\t */\n\t\tif (anchor.hasAttribute(this.#ignoreAttribute)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!shouldRouterHandleClick(event, anchor)) {\n\t\t\treturn;\n\t\t}\n\n\t\tevent.preventDefault();\n\t\tlet href = anchor.getAttribute(\"href\")?.trim();\n\t\tif (!href?.startsWith(\"/\")) {\n\t\t\thref = \"/\" + href;\n\t\t}\n\n\t\tthis.navigateTo(href);\n\t}\n\n\t/**\n\t * Add or remove search param to current url.\n\t * @param {HTMLInputElement} checkbox\n\t * @param {string} value\n\t * @returns void\n\t */\n\ttoggleParam(checkbox, value) {\n\t\tconst params = new URLSearchParams(location.search);\n\t\tconst name = checkbox.getAttribute(\"name\");\n\t\tif (!name) {\n\t\t\treturn console.warn(`name attribute is not set on ${checkbox.outerHTML}`);\n\t\t}\n\t\tif (checkbox.checked) {\n\t\t\t!params.has(name) && params.set(name, value);\n\t\t} else if (!checkbox.checked) {\n\t\t\tparams.has(name) && params.delete(name);\n\t\t}\n\n\t\tconst newUrl = !!params.size\n\t\t\t? `${location.pathname}?${params.toString()}`\n\t\t\t: location.pathname;\n\t\tthis.navigateTo(newUrl);\n\t}\n}\n"],"names":["addTrailingSlash","url","trim","endsWith","BreezeRouter","ignoreAttribute","isReady","isReadyCallbacks","previousPath","previousRoute","previousParams","currentPath","currentRoute","currentParams","constructor","config","this","_routes","window","addEventListener","_onChanged","bind","document","body","_handleClick","start","ready","fn","push","add","route","handler","console","warn","error","types","includes","split","filter","segment","forEach","type","match","replace","propName","replaceAll","path","navigateTo","history","pushState","redirect","async","location","pathname","params","_matchUrlToRoute","_handleRoute","dispatchEvent","CustomEvent","detail","router","name","toLowerCase","isAsyncFunction","matchedRoute","Object","entries","find","routeObject","length","routeSegments","slice","urlSegments","every","i","startsWith","Number","isInteger","decodeURIComponent","searchParams","URLSearchParams","search","event","anchor","composedPath","elem","tagName","hasAttribute","e","defaultPrevented","metaKey","ctrlKey","shiftKey","target","getAttribute","href","origin","shouldRouterHandleClick","preventDefault","toggleParam","checkbox","value","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.4.0",
3
+ "version": "1.0.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",