@bjornlu/svelte-router
Advanced tools
Comparing version 0.1.2 to 0.2.0
# Changelog | ||
## v0.1.2 - 2020-08-26 | ||
## 0.2.0 - 2020-08-30 | ||
### Added | ||
- Add `createLink` function to enable custom link creation | ||
- Format URL on page load | ||
### Changed | ||
- Rename `history` mode to `path` | ||
- All router options need to be defined | ||
- All public APIs, except for `route`, will now throw an error if called before `initRouter` | ||
- `initRouter` will throw an error if called more than once | ||
- `LocationInput`'s `path`, `search` and `hash` will treat empty string as undefined | ||
### Removed | ||
- Remove `link` action, use `createLink` instead | ||
### Fixed | ||
- Fix synchronous redirect rendering | ||
## 0.1.2 - 2020-08-26 | ||
### Fixed | ||
- Route store is always defined even before `initRouter` | ||
@@ -11,3 +34,3 @@ - `navigate` will not throw error if called before `initRouter` | ||
## v0.1.1 - 2020-08-25 | ||
## 0.1.1 - 2020-08-25 | ||
@@ -18,4 +41,4 @@ ### Fixed | ||
## v0.1.0 - 2020-08-25 | ||
## 0.1.0 - 2020-08-25 | ||
Initial release |
@@ -0,6 +1,6 @@ | ||
import './location-change-shim'; | ||
export { default as RouterView } from './RouterView.svelte'; | ||
export { default as Link } from './Link.svelte'; | ||
export { link } from './link'; | ||
export { navigate } from './navigate'; | ||
export { initRouter, route, Route, RouterOptions } from './router'; | ||
export { LinkState, Route } from './router/base'; | ||
export { LocationInput, RouteRecord, RedirectOption } from './types'; | ||
export { route, createLink, navigate, initRouter, RouterOptions } from './global'; |
@@ -0,5 +1,4 @@ | ||
import './location-change-shim'; | ||
export { default as RouterView } from './RouterView.svelte'; | ||
export { default as Link } from './Link.svelte'; | ||
export { link } from './link'; | ||
export { navigate } from './navigate'; | ||
export { initRouter, route } from './router'; | ||
export { route, createLink, navigate, initRouter } from './global'; |
@@ -14,3 +14,3 @@ import { RouteRecord } from './types'; | ||
export declare class RouteMatcher { | ||
private matchDatas; | ||
private readonly matchDatas; | ||
constructor(routes?: RouteRecord[]); | ||
@@ -17,0 +17,0 @@ /** Finds a route based on target path and the computed matchers. */ |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global.Router = {})); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global.Router = {})); | ||
}(this, (function (exports) { 'use strict'; | ||
function noop() { } | ||
function assign(tar, src) { | ||
// @ts-ignore | ||
for (const k in src) | ||
tar[k] = src[k]; | ||
return tar; | ||
} | ||
function run(fn) { | ||
return fn(); | ||
} | ||
function blank_object() { | ||
return Object.create(null); | ||
} | ||
function run_all(fns) { | ||
fns.forEach(run); | ||
} | ||
function is_function(thing) { | ||
return typeof thing === 'function'; | ||
} | ||
function safe_not_equal(a, b) { | ||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); | ||
} | ||
function subscribe(store, ...callbacks) { | ||
if (store == null) { | ||
return noop; | ||
} | ||
const unsub = store.subscribe(...callbacks); | ||
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; | ||
} | ||
function component_subscribe(component, store, callback) { | ||
component.$$.on_destroy.push(subscribe(store, callback)); | ||
} | ||
function create_slot(definition, ctx, $$scope, fn) { | ||
if (definition) { | ||
const slot_ctx = get_slot_context(definition, ctx, $$scope, fn); | ||
return definition[0](slot_ctx); | ||
} | ||
} | ||
function get_slot_context(definition, ctx, $$scope, fn) { | ||
return definition[1] && fn | ||
? assign($$scope.ctx.slice(), definition[1](fn(ctx))) | ||
: $$scope.ctx; | ||
} | ||
function get_slot_changes(definition, $$scope, dirty, fn) { | ||
if (definition[2] && fn) { | ||
const lets = definition[2](fn(dirty)); | ||
if ($$scope.dirty === undefined) { | ||
return lets; | ||
} | ||
if (typeof lets === 'object') { | ||
const merged = []; | ||
const len = Math.max($$scope.dirty.length, lets.length); | ||
for (let i = 0; i < len; i += 1) { | ||
merged[i] = $$scope.dirty[i] | lets[i]; | ||
} | ||
return merged; | ||
} | ||
return $$scope.dirty | lets; | ||
} | ||
return $$scope.dirty; | ||
} | ||
function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) { | ||
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); | ||
if (slot_changes) { | ||
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); | ||
slot.p(slot_context, slot_changes); | ||
} | ||
} | ||
function exclude_internal_props(props) { | ||
const result = {}; | ||
for (const k in props) | ||
if (k[0] !== '$') | ||
result[k] = props[k]; | ||
return result; | ||
} | ||
function compute_rest_props(props, keys) { | ||
const rest = {}; | ||
keys = new Set(keys); | ||
for (const k in props) | ||
if (!keys.has(k) && k[0] !== '$') | ||
rest[k] = props[k]; | ||
return rest; | ||
} | ||
function insert(target, node, anchor) { | ||
target.insertBefore(node, anchor || null); | ||
} | ||
function detach(node) { | ||
node.parentNode.removeChild(node); | ||
} | ||
function element(name) { | ||
return document.createElement(name); | ||
} | ||
function text(data) { | ||
return document.createTextNode(data); | ||
} | ||
function empty() { | ||
return text(''); | ||
} | ||
function listen(node, event, handler, options) { | ||
node.addEventListener(event, handler, options); | ||
return () => node.removeEventListener(event, handler, options); | ||
} | ||
function prevent_default(fn) { | ||
return function (event) { | ||
event.preventDefault(); | ||
// @ts-ignore | ||
return fn.call(this, event); | ||
}; | ||
} | ||
function attr(node, attribute, value) { | ||
if (value == null) | ||
node.removeAttribute(attribute); | ||
else if (node.getAttribute(attribute) !== value) | ||
node.setAttribute(attribute, value); | ||
} | ||
function set_attributes(node, attributes) { | ||
// @ts-ignore | ||
const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); | ||
for (const key in attributes) { | ||
if (attributes[key] == null) { | ||
node.removeAttribute(key); | ||
} | ||
else if (key === 'style') { | ||
node.style.cssText = attributes[key]; | ||
} | ||
else if (key === '__value') { | ||
node.value = node[key] = attributes[key]; | ||
} | ||
else if (descriptors[key] && descriptors[key].set) { | ||
node[key] = attributes[key]; | ||
} | ||
else { | ||
attr(node, key, attributes[key]); | ||
} | ||
} | ||
} | ||
function children(element) { | ||
return Array.from(element.childNodes); | ||
} | ||
function toggle_class(element, name, toggle) { | ||
element.classList[toggle ? 'add' : 'remove'](name); | ||
} | ||
/* | ||
Enable listening to all location changes that may be triggered by `pushState`, | ||
`replaceState`, `onpopstatte` and `onhashchange`. This enables the router | ||
to update whenever needed. | ||
let current_component; | ||
function set_current_component(component) { | ||
current_component = component; | ||
} | ||
No side-effects introduced. | ||
*/ | ||
const LOCATION_CHANGE = 'svelterouterlocationchange'; | ||
const originalPushState = history.pushState; | ||
const originalReplaceState = history.replaceState; | ||
const dispatch = () => window.dispatchEvent(new CustomEvent(LOCATION_CHANGE)); | ||
history.pushState = function (...args) { | ||
originalPushState.apply(this, args); | ||
dispatch(); | ||
}; | ||
history.replaceState = function (...args) { | ||
originalReplaceState.apply(this, args); | ||
dispatch(); | ||
}; | ||
window.addEventListener('popstate', dispatch); | ||
window.addEventListener('hashchange', dispatch); | ||
const dirty_components = []; | ||
const binding_callbacks = []; | ||
const render_callbacks = []; | ||
const flush_callbacks = []; | ||
const resolved_promise = Promise.resolve(); | ||
let update_scheduled = false; | ||
function schedule_update() { | ||
if (!update_scheduled) { | ||
update_scheduled = true; | ||
resolved_promise.then(flush); | ||
} | ||
} | ||
function add_render_callback(fn) { | ||
render_callbacks.push(fn); | ||
} | ||
let flushing = false; | ||
const seen_callbacks = new Set(); | ||
function flush() { | ||
if (flushing) | ||
return; | ||
flushing = true; | ||
do { | ||
// first, call beforeUpdate functions | ||
// and update components | ||
for (let i = 0; i < dirty_components.length; i += 1) { | ||
const component = dirty_components[i]; | ||
set_current_component(component); | ||
update(component.$$); | ||
} | ||
dirty_components.length = 0; | ||
while (binding_callbacks.length) | ||
binding_callbacks.pop()(); | ||
// then, once components are updated, call | ||
// afterUpdate functions. This may cause | ||
// subsequent updates... | ||
for (let i = 0; i < render_callbacks.length; i += 1) { | ||
const callback = render_callbacks[i]; | ||
if (!seen_callbacks.has(callback)) { | ||
// ...so guard against infinite loops | ||
seen_callbacks.add(callback); | ||
callback(); | ||
} | ||
} | ||
render_callbacks.length = 0; | ||
} while (dirty_components.length); | ||
while (flush_callbacks.length) { | ||
flush_callbacks.pop()(); | ||
} | ||
update_scheduled = false; | ||
flushing = false; | ||
seen_callbacks.clear(); | ||
} | ||
function update($$) { | ||
if ($$.fragment !== null) { | ||
$$.update(); | ||
run_all($$.before_update); | ||
const dirty = $$.dirty; | ||
$$.dirty = [-1]; | ||
$$.fragment && $$.fragment.p($$.ctx, dirty); | ||
$$.after_update.forEach(add_render_callback); | ||
} | ||
} | ||
const outroing = new Set(); | ||
let outros; | ||
function group_outros() { | ||
outros = { | ||
r: 0, | ||
c: [], | ||
p: outros // parent group | ||
}; | ||
} | ||
function check_outros() { | ||
if (!outros.r) { | ||
run_all(outros.c); | ||
} | ||
outros = outros.p; | ||
} | ||
function transition_in(block, local) { | ||
if (block && block.i) { | ||
outroing.delete(block); | ||
block.i(local); | ||
} | ||
} | ||
function transition_out(block, local, detach, callback) { | ||
if (block && block.o) { | ||
if (outroing.has(block)) | ||
return; | ||
outroing.add(block); | ||
outros.c.push(() => { | ||
outroing.delete(block); | ||
if (callback) { | ||
if (detach) | ||
block.d(1); | ||
callback(); | ||
} | ||
}); | ||
block.o(local); | ||
} | ||
} | ||
function noop() { } | ||
function assign(tar, src) { | ||
// @ts-ignore | ||
for (const k in src) | ||
tar[k] = src[k]; | ||
return tar; | ||
} | ||
function run(fn) { | ||
return fn(); | ||
} | ||
function blank_object() { | ||
return Object.create(null); | ||
} | ||
function run_all(fns) { | ||
fns.forEach(run); | ||
} | ||
function is_function(thing) { | ||
return typeof thing === 'function'; | ||
} | ||
function safe_not_equal(a, b) { | ||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); | ||
} | ||
function subscribe(store, ...callbacks) { | ||
if (store == null) { | ||
return noop; | ||
} | ||
const unsub = store.subscribe(...callbacks); | ||
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; | ||
} | ||
function get_store_value(store) { | ||
let value; | ||
subscribe(store, _ => value = _)(); | ||
return value; | ||
} | ||
function component_subscribe(component, store, callback) { | ||
component.$$.on_destroy.push(subscribe(store, callback)); | ||
} | ||
function create_slot(definition, ctx, $$scope, fn) { | ||
if (definition) { | ||
const slot_ctx = get_slot_context(definition, ctx, $$scope, fn); | ||
return definition[0](slot_ctx); | ||
} | ||
} | ||
function get_slot_context(definition, ctx, $$scope, fn) { | ||
return definition[1] && fn | ||
? assign($$scope.ctx.slice(), definition[1](fn(ctx))) | ||
: $$scope.ctx; | ||
} | ||
function get_slot_changes(definition, $$scope, dirty, fn) { | ||
if (definition[2] && fn) { | ||
const lets = definition[2](fn(dirty)); | ||
if ($$scope.dirty === undefined) { | ||
return lets; | ||
} | ||
if (typeof lets === 'object') { | ||
const merged = []; | ||
const len = Math.max($$scope.dirty.length, lets.length); | ||
for (let i = 0; i < len; i += 1) { | ||
merged[i] = $$scope.dirty[i] | lets[i]; | ||
} | ||
return merged; | ||
} | ||
return $$scope.dirty | lets; | ||
} | ||
return $$scope.dirty; | ||
} | ||
function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) { | ||
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); | ||
if (slot_changes) { | ||
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); | ||
slot.p(slot_context, slot_changes); | ||
} | ||
} | ||
function exclude_internal_props(props) { | ||
const result = {}; | ||
for (const k in props) | ||
if (k[0] !== '$') | ||
result[k] = props[k]; | ||
return result; | ||
} | ||
function compute_rest_props(props, keys) { | ||
const rest = {}; | ||
keys = new Set(keys); | ||
for (const k in props) | ||
if (!keys.has(k) && k[0] !== '$') | ||
rest[k] = props[k]; | ||
return rest; | ||
} | ||
function insert(target, node, anchor) { | ||
target.insertBefore(node, anchor || null); | ||
} | ||
function detach(node) { | ||
node.parentNode.removeChild(node); | ||
} | ||
function element(name) { | ||
return document.createElement(name); | ||
} | ||
function text(data) { | ||
return document.createTextNode(data); | ||
} | ||
function empty() { | ||
return text(''); | ||
} | ||
function listen(node, event, handler, options) { | ||
node.addEventListener(event, handler, options); | ||
return () => node.removeEventListener(event, handler, options); | ||
} | ||
function prevent_default(fn) { | ||
return function (event) { | ||
event.preventDefault(); | ||
// @ts-ignore | ||
return fn.call(this, event); | ||
}; | ||
} | ||
function attr(node, attribute, value) { | ||
if (value == null) | ||
node.removeAttribute(attribute); | ||
else if (node.getAttribute(attribute) !== value) | ||
node.setAttribute(attribute, value); | ||
} | ||
function set_attributes(node, attributes) { | ||
// @ts-ignore | ||
const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); | ||
for (const key in attributes) { | ||
if (attributes[key] == null) { | ||
node.removeAttribute(key); | ||
} | ||
else if (key === 'style') { | ||
node.style.cssText = attributes[key]; | ||
} | ||
else if (key === '__value') { | ||
node.value = node[key] = attributes[key]; | ||
} | ||
else if (descriptors[key] && descriptors[key].set) { | ||
node[key] = attributes[key]; | ||
} | ||
else { | ||
attr(node, key, attributes[key]); | ||
} | ||
} | ||
} | ||
function children(element) { | ||
return Array.from(element.childNodes); | ||
} | ||
function toggle_class(element, name, toggle) { | ||
element.classList[toggle ? 'add' : 'remove'](name); | ||
} | ||
function get_spread_update(levels, updates) { | ||
const update = {}; | ||
const to_null_out = {}; | ||
const accounted_for = { $$scope: 1 }; | ||
let i = levels.length; | ||
while (i--) { | ||
const o = levels[i]; | ||
const n = updates[i]; | ||
if (n) { | ||
for (const key in o) { | ||
if (!(key in n)) | ||
to_null_out[key] = 1; | ||
} | ||
for (const key in n) { | ||
if (!accounted_for[key]) { | ||
update[key] = n[key]; | ||
accounted_for[key] = 1; | ||
} | ||
} | ||
levels[i] = n; | ||
} | ||
else { | ||
for (const key in o) { | ||
accounted_for[key] = 1; | ||
} | ||
} | ||
} | ||
for (const key in to_null_out) { | ||
if (!(key in update)) | ||
update[key] = undefined; | ||
} | ||
return update; | ||
} | ||
function create_component(block) { | ||
block && block.c(); | ||
} | ||
function mount_component(component, target, anchor) { | ||
const { fragment, on_mount, on_destroy, after_update } = component.$$; | ||
fragment && fragment.m(target, anchor); | ||
// onMount happens before the initial afterUpdate | ||
add_render_callback(() => { | ||
const new_on_destroy = on_mount.map(run).filter(is_function); | ||
if (on_destroy) { | ||
on_destroy.push(...new_on_destroy); | ||
} | ||
else { | ||
// Edge case - component was destroyed immediately, | ||
// most likely as a result of a binding initialising | ||
run_all(new_on_destroy); | ||
} | ||
component.$$.on_mount = []; | ||
}); | ||
after_update.forEach(add_render_callback); | ||
} | ||
function destroy_component(component, detaching) { | ||
const $$ = component.$$; | ||
if ($$.fragment !== null) { | ||
run_all($$.on_destroy); | ||
$$.fragment && $$.fragment.d(detaching); | ||
// TODO null out other refs, including component.$$ (but need to | ||
// preserve final state?) | ||
$$.on_destroy = $$.fragment = null; | ||
$$.ctx = []; | ||
} | ||
} | ||
function make_dirty(component, i) { | ||
if (component.$$.dirty[0] === -1) { | ||
dirty_components.push(component); | ||
schedule_update(); | ||
component.$$.dirty.fill(0); | ||
} | ||
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); | ||
} | ||
function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { | ||
const parent_component = current_component; | ||
set_current_component(component); | ||
const prop_values = options.props || {}; | ||
const $$ = component.$$ = { | ||
fragment: null, | ||
ctx: null, | ||
// state | ||
props, | ||
update: noop, | ||
not_equal, | ||
bound: blank_object(), | ||
// lifecycle | ||
on_mount: [], | ||
on_destroy: [], | ||
before_update: [], | ||
after_update: [], | ||
context: new Map(parent_component ? parent_component.$$.context : []), | ||
// everything else | ||
callbacks: blank_object(), | ||
dirty | ||
}; | ||
let ready = false; | ||
$$.ctx = instance | ||
? instance(component, prop_values, (i, ret, ...rest) => { | ||
const value = rest.length ? rest[0] : ret; | ||
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { | ||
if ($$.bound[i]) | ||
$$.bound[i](value); | ||
if (ready) | ||
make_dirty(component, i); | ||
} | ||
return ret; | ||
}) | ||
: []; | ||
$$.update(); | ||
ready = true; | ||
run_all($$.before_update); | ||
// `false` as a special case of no DOM component | ||
$$.fragment = create_fragment ? create_fragment($$.ctx) : false; | ||
if (options.target) { | ||
if (options.hydrate) { | ||
const nodes = children(options.target); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
$$.fragment && $$.fragment.l(nodes); | ||
nodes.forEach(detach); | ||
} | ||
else { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
$$.fragment && $$.fragment.c(); | ||
} | ||
if (options.intro) | ||
transition_in(component.$$.fragment); | ||
mount_component(component, options.target, options.anchor); | ||
flush(); | ||
} | ||
set_current_component(parent_component); | ||
} | ||
class SvelteComponent { | ||
$destroy() { | ||
destroy_component(this, 1); | ||
this.$destroy = noop; | ||
} | ||
$on(type, callback) { | ||
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); | ||
callbacks.push(callback); | ||
return () => { | ||
const index = callbacks.indexOf(callback); | ||
if (index !== -1) | ||
callbacks.splice(index, 1); | ||
}; | ||
} | ||
$set() { | ||
// overridden by instance, if it has props | ||
} | ||
} | ||
let current_component; | ||
function set_current_component(component) { | ||
current_component = component; | ||
} | ||
const subscriber_queue = []; | ||
/** | ||
* Creates a `Readable` store that allows reading by subscription. | ||
* @param value initial value | ||
* @param {StartStopNotifier}start start and stop notifications for subscriptions | ||
*/ | ||
function readable(value, start) { | ||
return { | ||
subscribe: writable(value, start).subscribe, | ||
}; | ||
} | ||
/** | ||
* Create a `Writable` store that allows both updating and reading by subscription. | ||
* @param {*=}value initial value | ||
* @param {StartStopNotifier=}start start and stop notifications for subscriptions | ||
*/ | ||
function writable(value, start = noop) { | ||
let stop; | ||
const subscribers = []; | ||
function set(new_value) { | ||
if (safe_not_equal(value, new_value)) { | ||
value = new_value; | ||
if (stop) { // store is ready | ||
const run_queue = !subscriber_queue.length; | ||
for (let i = 0; i < subscribers.length; i += 1) { | ||
const s = subscribers[i]; | ||
s[1](); | ||
subscriber_queue.push(s, value); | ||
} | ||
if (run_queue) { | ||
for (let i = 0; i < subscriber_queue.length; i += 2) { | ||
subscriber_queue[i][0](subscriber_queue[i + 1]); | ||
} | ||
subscriber_queue.length = 0; | ||
} | ||
} | ||
} | ||
} | ||
function update(fn) { | ||
set(fn(value)); | ||
} | ||
function subscribe(run, invalidate = noop) { | ||
const subscriber = [run, invalidate]; | ||
subscribers.push(subscriber); | ||
if (subscribers.length === 1) { | ||
stop = start(set) || noop; | ||
} | ||
run(value); | ||
return () => { | ||
const index = subscribers.indexOf(subscriber); | ||
if (index !== -1) { | ||
subscribers.splice(index, 1); | ||
} | ||
if (subscribers.length === 0) { | ||
stop(); | ||
stop = null; | ||
} | ||
}; | ||
} | ||
return { set, update, subscribe }; | ||
} | ||
const dirty_components = []; | ||
const binding_callbacks = []; | ||
const render_callbacks = []; | ||
const flush_callbacks = []; | ||
const resolved_promise = Promise.resolve(); | ||
let update_scheduled = false; | ||
function schedule_update() { | ||
if (!update_scheduled) { | ||
update_scheduled = true; | ||
resolved_promise.then(flush); | ||
} | ||
} | ||
function tick() { | ||
schedule_update(); | ||
return resolved_promise; | ||
} | ||
function add_render_callback(fn) { | ||
render_callbacks.push(fn); | ||
} | ||
let flushing = false; | ||
const seen_callbacks = new Set(); | ||
function flush() { | ||
if (flushing) | ||
return; | ||
flushing = true; | ||
do { | ||
// first, call beforeUpdate functions | ||
// and update components | ||
for (let i = 0; i < dirty_components.length; i += 1) { | ||
const component = dirty_components[i]; | ||
set_current_component(component); | ||
update(component.$$); | ||
} | ||
dirty_components.length = 0; | ||
while (binding_callbacks.length) | ||
binding_callbacks.pop()(); | ||
// then, once components are updated, call | ||
// afterUpdate functions. This may cause | ||
// subsequent updates... | ||
for (let i = 0; i < render_callbacks.length; i += 1) { | ||
const callback = render_callbacks[i]; | ||
if (!seen_callbacks.has(callback)) { | ||
// ...so guard against infinite loops | ||
seen_callbacks.add(callback); | ||
callback(); | ||
} | ||
} | ||
render_callbacks.length = 0; | ||
} while (dirty_components.length); | ||
while (flush_callbacks.length) { | ||
flush_callbacks.pop()(); | ||
} | ||
update_scheduled = false; | ||
flushing = false; | ||
seen_callbacks.clear(); | ||
} | ||
function update($$) { | ||
if ($$.fragment !== null) { | ||
$$.update(); | ||
run_all($$.before_update); | ||
const dirty = $$.dirty; | ||
$$.dirty = [-1]; | ||
$$.fragment && $$.fragment.p($$.ctx, dirty); | ||
$$.after_update.forEach(add_render_callback); | ||
} | ||
} | ||
const outroing = new Set(); | ||
let outros; | ||
function group_outros() { | ||
outros = { | ||
r: 0, | ||
c: [], | ||
p: outros // parent group | ||
}; | ||
} | ||
function check_outros() { | ||
if (!outros.r) { | ||
run_all(outros.c); | ||
} | ||
outros = outros.p; | ||
} | ||
function transition_in(block, local) { | ||
if (block && block.i) { | ||
outroing.delete(block); | ||
block.i(local); | ||
} | ||
} | ||
function transition_out(block, local, detach, callback) { | ||
if (block && block.o) { | ||
if (outroing.has(block)) | ||
return; | ||
outroing.add(block); | ||
outros.c.push(() => { | ||
outroing.delete(block); | ||
if (callback) { | ||
if (detach) | ||
block.d(1); | ||
callback(); | ||
} | ||
}); | ||
block.o(local); | ||
} | ||
} | ||
/* | ||
Enable listening to all location changes that may be triggered by `pushState`, | ||
`replaceState`, `onpopstatte` and `onhashchange`. This enables the router | ||
to update whenever needed. | ||
function get_spread_update(levels, updates) { | ||
const update = {}; | ||
const to_null_out = {}; | ||
const accounted_for = { $$scope: 1 }; | ||
let i = levels.length; | ||
while (i--) { | ||
const o = levels[i]; | ||
const n = updates[i]; | ||
if (n) { | ||
for (const key in o) { | ||
if (!(key in n)) | ||
to_null_out[key] = 1; | ||
} | ||
for (const key in n) { | ||
if (!accounted_for[key]) { | ||
update[key] = n[key]; | ||
accounted_for[key] = 1; | ||
} | ||
} | ||
levels[i] = n; | ||
} | ||
else { | ||
for (const key in o) { | ||
accounted_for[key] = 1; | ||
} | ||
} | ||
} | ||
for (const key in to_null_out) { | ||
if (!(key in update)) | ||
update[key] = undefined; | ||
} | ||
return update; | ||
} | ||
function create_component(block) { | ||
block && block.c(); | ||
} | ||
function mount_component(component, target, anchor) { | ||
const { fragment, on_mount, on_destroy, after_update } = component.$$; | ||
fragment && fragment.m(target, anchor); | ||
// onMount happens before the initial afterUpdate | ||
add_render_callback(() => { | ||
const new_on_destroy = on_mount.map(run).filter(is_function); | ||
if (on_destroy) { | ||
on_destroy.push(...new_on_destroy); | ||
} | ||
else { | ||
// Edge case - component was destroyed immediately, | ||
// most likely as a result of a binding initialising | ||
run_all(new_on_destroy); | ||
} | ||
component.$$.on_mount = []; | ||
}); | ||
after_update.forEach(add_render_callback); | ||
} | ||
function destroy_component(component, detaching) { | ||
const $$ = component.$$; | ||
if ($$.fragment !== null) { | ||
run_all($$.on_destroy); | ||
$$.fragment && $$.fragment.d(detaching); | ||
// TODO null out other refs, including component.$$ (but need to | ||
// preserve final state?) | ||
$$.on_destroy = $$.fragment = null; | ||
$$.ctx = []; | ||
} | ||
} | ||
function make_dirty(component, i) { | ||
if (component.$$.dirty[0] === -1) { | ||
dirty_components.push(component); | ||
schedule_update(); | ||
component.$$.dirty.fill(0); | ||
} | ||
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); | ||
} | ||
function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { | ||
const parent_component = current_component; | ||
set_current_component(component); | ||
const prop_values = options.props || {}; | ||
const $$ = component.$$ = { | ||
fragment: null, | ||
ctx: null, | ||
// state | ||
props, | ||
update: noop, | ||
not_equal, | ||
bound: blank_object(), | ||
// lifecycle | ||
on_mount: [], | ||
on_destroy: [], | ||
before_update: [], | ||
after_update: [], | ||
context: new Map(parent_component ? parent_component.$$.context : []), | ||
// everything else | ||
callbacks: blank_object(), | ||
dirty | ||
}; | ||
let ready = false; | ||
$$.ctx = instance | ||
? instance(component, prop_values, (i, ret, ...rest) => { | ||
const value = rest.length ? rest[0] : ret; | ||
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { | ||
if ($$.bound[i]) | ||
$$.bound[i](value); | ||
if (ready) | ||
make_dirty(component, i); | ||
} | ||
return ret; | ||
}) | ||
: []; | ||
$$.update(); | ||
ready = true; | ||
run_all($$.before_update); | ||
// `false` as a special case of no DOM component | ||
$$.fragment = create_fragment ? create_fragment($$.ctx) : false; | ||
if (options.target) { | ||
if (options.hydrate) { | ||
const nodes = children(options.target); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
$$.fragment && $$.fragment.l(nodes); | ||
nodes.forEach(detach); | ||
} | ||
else { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
$$.fragment && $$.fragment.c(); | ||
} | ||
if (options.intro) | ||
transition_in(component.$$.fragment); | ||
mount_component(component, options.target, options.anchor); | ||
flush(); | ||
} | ||
set_current_component(parent_component); | ||
} | ||
class SvelteComponent { | ||
$destroy() { | ||
destroy_component(this, 1); | ||
this.$destroy = noop; | ||
} | ||
$on(type, callback) { | ||
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); | ||
callbacks.push(callback); | ||
return () => { | ||
const index = callbacks.indexOf(callback); | ||
if (index !== -1) | ||
callbacks.splice(index, 1); | ||
}; | ||
} | ||
$set() { | ||
// overridden by instance, if it has props | ||
} | ||
} | ||
No side-effects introduced. | ||
*/ | ||
const LOCATION_CHANGE = 'svelterouterlocationchange'; | ||
const originalPushState = history.pushState; | ||
const originalReplaceState = history.replaceState; | ||
const dispatch = () => window.dispatchEvent(new CustomEvent(LOCATION_CHANGE)); | ||
history.pushState = function (...args) { | ||
originalPushState.apply(this, args); | ||
dispatch(); | ||
}; | ||
history.replaceState = function (...args) { | ||
originalReplaceState.apply(this, args); | ||
dispatch(); | ||
}; | ||
window.addEventListener('popstate', dispatch); | ||
window.addEventListener('hashchange', dispatch); | ||
const subscriber_queue = []; | ||
/** | ||
* Creates a `Readable` store that allows reading by subscription. | ||
* @param value initial value | ||
* @param {StartStopNotifier}start start and stop notifications for subscriptions | ||
*/ | ||
function readable(value, start) { | ||
return { | ||
subscribe: writable(value, start).subscribe, | ||
}; | ||
} | ||
/** | ||
* Create a `Writable` store that allows both updating and reading by subscription. | ||
* @param {*=}value initial value | ||
* @param {StartStopNotifier=}start start and stop notifications for subscriptions | ||
*/ | ||
function writable(value, start = noop) { | ||
let stop; | ||
const subscribers = []; | ||
function set(new_value) { | ||
if (safe_not_equal(value, new_value)) { | ||
value = new_value; | ||
if (stop) { // store is ready | ||
const run_queue = !subscriber_queue.length; | ||
for (let i = 0; i < subscribers.length; i += 1) { | ||
const s = subscribers[i]; | ||
s[1](); | ||
subscriber_queue.push(s, value); | ||
} | ||
if (run_queue) { | ||
for (let i = 0; i < subscriber_queue.length; i += 2) { | ||
subscriber_queue[i][0](subscriber_queue[i + 1]); | ||
} | ||
subscriber_queue.length = 0; | ||
} | ||
} | ||
} | ||
} | ||
function update(fn) { | ||
set(fn(value)); | ||
} | ||
function subscribe(run, invalidate = noop) { | ||
const subscriber = [run, invalidate]; | ||
subscribers.push(subscriber); | ||
if (subscribers.length === 1) { | ||
stop = start(set) || noop; | ||
} | ||
run(value); | ||
return () => { | ||
const index = subscribers.indexOf(subscriber); | ||
if (index !== -1) { | ||
subscribers.splice(index, 1); | ||
} | ||
if (subscribers.length === 0) { | ||
stop(); | ||
stop = null; | ||
} | ||
}; | ||
} | ||
return { set, update, subscribe }; | ||
} | ||
function derived(stores, fn, initial_value) { | ||
const single = !Array.isArray(stores); | ||
const stores_array = single | ||
? [stores] | ||
: stores; | ||
const auto = fn.length < 2; | ||
return readable(initial_value, (set) => { | ||
let inited = false; | ||
const values = []; | ||
let pending = 0; | ||
let cleanup = noop; | ||
const sync = () => { | ||
if (pending) { | ||
return; | ||
} | ||
cleanup(); | ||
const result = fn(single ? values[0] : values, set); | ||
if (auto) { | ||
set(result); | ||
} | ||
else { | ||
cleanup = is_function(result) ? result : noop; | ||
} | ||
}; | ||
const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => { | ||
values[i] = value; | ||
pending &= ~(1 << i); | ||
if (inited) { | ||
sync(); | ||
} | ||
}, () => { | ||
pending |= (1 << i); | ||
})); | ||
inited = true; | ||
sync(); | ||
return function stop() { | ||
run_all(unsubscribers); | ||
cleanup(); | ||
}; | ||
}); | ||
} | ||
/** Makes sure path has leading "/" and no trailing "/" */ | ||
function formatPath(path) { | ||
if (path.endsWith('/')) { | ||
path = path.slice(0, -1); | ||
} | ||
if (!path.startsWith('/')) { | ||
path = '/' + path; | ||
} | ||
return path; | ||
} | ||
/** Joins multiple path and also formats it */ | ||
function joinPaths(...paths) { | ||
return paths | ||
.map(formatPath) | ||
.filter((v) => v !== '/') | ||
.join(''); | ||
} | ||
function addTrailingSlash(path) { | ||
if (!path.endsWith('/')) { | ||
path += '/'; | ||
} | ||
return path; | ||
} | ||
function handleThunk(thunk) { | ||
return typeof thunk === 'function' ? thunk() : thunk; | ||
} | ||
function handlePromisable(promisable, callback) { | ||
if (promisable instanceof Promise) { | ||
promisable.then(callback); | ||
} | ||
else { | ||
callback(promisable); | ||
} | ||
} | ||
/** Attribute is true only if it's not null and value not "false" */ | ||
function isAttributeTrue(el, attrName) { | ||
const attr = el.getAttribute(attrName); | ||
return attr != null && attr !== 'false'; | ||
} | ||
function regexparam (str, loose) { | ||
if (str instanceof RegExp) return { keys:false, pattern:str }; | ||
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/'); | ||
arr[0] || arr.shift(); | ||
const DUMMY_LOCATION = { | ||
path: '', | ||
search: new URLSearchParams(), | ||
hash: '' | ||
}; | ||
while (tmp = arr.shift()) { | ||
c = tmp[0]; | ||
if (c === '*') { | ||
keys.push('wild'); | ||
pattern += '/(.*)'; | ||
} else if (c === ':') { | ||
o = tmp.indexOf('?', 1); | ||
ext = tmp.indexOf('.', 1); | ||
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) ); | ||
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)'; | ||
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext); | ||
} else { | ||
pattern += '/' + tmp; | ||
} | ||
} | ||
class HashHistory { | ||
constructor() { | ||
this.currentLocation = readable(DUMMY_LOCATION, (set) => { | ||
const handleLocationChange = () => { | ||
set({ | ||
path: formatPath(window.location.hash.slice(1)), | ||
search: new URLSearchParams(window.location.search), | ||
hash: window.location.hash | ||
}); | ||
}; | ||
handleLocationChange(); | ||
window.addEventListener(LOCATION_CHANGE, handleLocationChange); | ||
return () => window.removeEventListener(LOCATION_CHANGE, handleLocationChange); | ||
}); | ||
} | ||
go(delta) { | ||
history.go(delta); | ||
} | ||
push(to) { | ||
const href = this.createHref(to); | ||
history.pushState(href, '', href); | ||
} | ||
replace(to) { | ||
const href = this.createHref(to); | ||
history.replaceState(href, '', href); | ||
} | ||
createHref(to) { | ||
const url = new URL(window.location.href); | ||
if (to.hash != null) { | ||
url.hash = to.hash; | ||
} | ||
if (to.path != null) { | ||
url.hash = '#' + to.path; | ||
} | ||
url.search = | ||
to.search != null ? '?' + new URLSearchParams(to.search).toString() : ''; | ||
return url.toString(); | ||
} | ||
} | ||
return { | ||
keys: keys, | ||
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i') | ||
}; | ||
} | ||
const basePath = document.getElementsByTagName('base').length > 0 | ||
? document.baseURI.replace(window.location.origin, '') | ||
: '/'; | ||
const basePath = document.getElementsByTagName('base').length > 0 | ||
? document.baseURI.replace(window.location.origin, '') | ||
: '/'; | ||
function parseLocationInput(to) { | ||
const url = new URL(to, 'https://example.com'); | ||
return { | ||
path: to.startsWith('/') ? url.pathname : undefined, | ||
search: url.search, | ||
hash: url.hash | ||
}; | ||
} | ||
/** Replace named param in path, e.g. `/foo/:id` => `/foo/123` */ | ||
function replacePathParams(path, params) { | ||
return path.replace(/:([^/]+)/g, (o, v) => { var _a; return (_a = params[v]) !== null && _a !== void 0 ? _a : o; }); | ||
} | ||
/** Makes sure path has leading "/" and no trailing "/" */ | ||
function formatPath(path) { | ||
if (path.endsWith('/')) { | ||
path = path.slice(0, -1); | ||
} | ||
if (!path.startsWith('/')) { | ||
path = '/' + path; | ||
} | ||
return path; | ||
} | ||
/** Joins multiple path and also formats it */ | ||
function joinPaths(...paths) { | ||
return paths | ||
.map(formatPath) | ||
.filter((v) => v !== '/') | ||
.join(''); | ||
} | ||
function addTrailingSlash(path) { | ||
if (!path.endsWith('/')) { | ||
path += '/'; | ||
} | ||
return path; | ||
} | ||
function handleThunk(thunk) { | ||
return typeof thunk === 'function' ? thunk() : thunk; | ||
} | ||
function handlePromisable(promisable, callback) { | ||
if (promisable instanceof Promise) { | ||
promisable.then(callback); | ||
} | ||
else { | ||
callback(promisable); | ||
} | ||
} | ||
class HtmlHistory { | ||
constructor() { | ||
this.currentLocation = readable(DUMMY_LOCATION, (set) => { | ||
const handleLocationChange = () => { | ||
set({ | ||
path: formatPath(window.location.pathname.replace(basePath, '')), | ||
search: new URLSearchParams(window.location.search), | ||
hash: window.location.hash | ||
}); | ||
}; | ||
handleLocationChange(); | ||
window.addEventListener(LOCATION_CHANGE, handleLocationChange); | ||
return () => window.removeEventListener(LOCATION_CHANGE, handleLocationChange); | ||
}); | ||
} | ||
go(delta) { | ||
history.go(delta); | ||
} | ||
push(to) { | ||
const href = this.createHref(to); | ||
history.pushState(href, '', href); | ||
} | ||
replace(to) { | ||
const href = this.createHref(to); | ||
history.replaceState(href, '', href); | ||
} | ||
createHref(to) { | ||
var _a; | ||
const url = new URL(window.location.href); | ||
if (to.path != null) { | ||
url.pathname = joinPaths(basePath, to.path); | ||
} | ||
url.search = | ||
to.search != null ? '?' + new URLSearchParams(to.search).toString() : ''; | ||
url.hash = (_a = to.hash) !== null && _a !== void 0 ? _a : ''; | ||
return url.toString(); | ||
} | ||
} | ||
class RouteMatcher { | ||
constructor(routes = []) { | ||
this.matchDatas = []; | ||
this.buildDatas(routes); | ||
} | ||
/** Finds a route based on target path and the computed matchers. */ | ||
matchRoute(path) { | ||
// Add trailing slash to route path so it properly matches nested routes too. | ||
// e.g. /foo should match /foo/* | ||
const matchPath = addTrailingSlash(path); | ||
for (const matchData of this.matchDatas) { | ||
const params = {}; | ||
const matchResult = matchPath.match(matchData.pattern); | ||
if (matchResult) { | ||
for (let i = 0; i < matchData.keys.length; i++) { | ||
params[matchData.keys[i]] = matchResult[i + 1]; | ||
} | ||
if ('wild' in params) { | ||
params.wild = formatPath(params.wild); | ||
} | ||
return { | ||
path, | ||
params, | ||
matched: matchData.matched | ||
}; | ||
} | ||
} | ||
} | ||
/** | ||
* Convert the routes as datas that contains information for path matching. | ||
* This will recursively traverse child routes and flatten into `matchDatas`. | ||
*/ | ||
buildDatas(routes, parentData = { path: '', matched: [] }) { | ||
routes.forEach((route) => { | ||
var _a; | ||
// Cumulative metadata when traversing parents | ||
const parent = { | ||
path: joinPaths(parentData.path, route.path), | ||
matched: parentData.matched.concat(route) | ||
}; | ||
if ((_a = route.children) === null || _a === void 0 ? void 0 : _a.length) { | ||
this.buildDatas(route.children, parent); | ||
} | ||
else { | ||
this.matchDatas.push(Object.assign(Object.assign({}, parent), regexparam(parent.path))); | ||
} | ||
}); | ||
// Make sure "/" doesn't fall through if the routes' paths do. | ||
// Example route config: | ||
// "/foo" | ||
// - "/bar" | ||
// match('/foo') should render "/foo" component but with empty child | ||
if (!routes.some((route) => ['', '/', '*', '/*'].includes(route.path))) { | ||
this.matchDatas.push(Object.assign(Object.assign({}, parentData), regexparam(parentData.path))); | ||
} | ||
} | ||
} | ||
function regexparam (str, loose) { | ||
if (str instanceof RegExp) return { keys:false, pattern:str }; | ||
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/'); | ||
arr[0] || arr.shift(); | ||
class Router { | ||
constructor(routes) { | ||
this.routeMatcher = new RouteMatcher(routes); | ||
this.currentRoute = readable(this.getCurrentRoute(), (set) => { | ||
const handleChange = () => set(this.getCurrentRoute()); | ||
window.addEventListener(LOCATION_CHANGE, handleChange); | ||
return () => window.removeEventListener(LOCATION_CHANGE, handleChange); | ||
}); | ||
// Format URL on page load | ||
this.navigate(this.getCurrentLocationInput(), true); | ||
} | ||
navigate(to, replace = false) { | ||
if (typeof to === 'number') { | ||
history.go(to); | ||
return; | ||
} | ||
if (typeof to === 'string') { | ||
to = parseLocationInput(to); | ||
} | ||
const url = this.createUrl(this.replaceParams(to)); | ||
if (replace) { | ||
history.replaceState(url, '', url); | ||
} | ||
else { | ||
history.pushState(url, '', url); | ||
} | ||
} | ||
createLink(to) { | ||
const input = typeof to === 'string' ? parseLocationInput(to) : to; | ||
return derived(this.currentRoute, ($currentRoute) => { | ||
const replacedInput = this.replaceParams(input, $currentRoute.params); | ||
const href = this.createLinkHref(replacedInput); | ||
const path = this.getPath(replacedInput); | ||
if (path == null) { | ||
return { | ||
href, | ||
isActive: false, | ||
isExactActive: false | ||
}; | ||
} | ||
const formattedPath = formatPath(path); | ||
const routePath = $currentRoute.path; | ||
return { | ||
href, | ||
isActive: routePath.startsWith(formattedPath), | ||
isExactActive: routePath === formattedPath | ||
}; | ||
}); | ||
} | ||
getCurrentRoute() { | ||
var _a, _b; | ||
const currentPath = this.getCurrentPath(); | ||
const matchedRoute = this.routeMatcher.matchRoute(currentPath); | ||
return { | ||
path: currentPath, | ||
search: new URLSearchParams(window.location.search), | ||
hash: window.location.hash, | ||
params: (_a = matchedRoute === null || matchedRoute === void 0 ? void 0 : matchedRoute.params) !== null && _a !== void 0 ? _a : {}, | ||
matched: (_b = matchedRoute === null || matchedRoute === void 0 ? void 0 : matchedRoute.matched) !== null && _b !== void 0 ? _b : [] | ||
}; | ||
} | ||
} | ||
while (tmp = arr.shift()) { | ||
c = tmp[0]; | ||
if (c === '*') { | ||
keys.push('wild'); | ||
pattern += '/(.*)'; | ||
} else if (c === ':') { | ||
o = tmp.indexOf('?', 1); | ||
ext = tmp.indexOf('.', 1); | ||
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) ); | ||
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)'; | ||
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext); | ||
} else { | ||
pattern += '/' + tmp; | ||
} | ||
} | ||
class HashRouter extends Router { | ||
getCurrentPath() { | ||
return formatPath(window.location.hash.slice(1)); | ||
} | ||
getCurrentLocationInput() { | ||
return { | ||
hash: '#' + this.getCurrentPath(), | ||
search: window.location.search | ||
}; | ||
} | ||
getPath(to) { | ||
var _a, _b; | ||
return (_a = to.path) !== null && _a !== void 0 ? _a : (_b = to.hash) === null || _b === void 0 ? void 0 : _b.slice(1); | ||
} | ||
createUrl(to) { | ||
var _a; | ||
const url = new URL(window.location.href); | ||
if (to.path) { | ||
url.hash = '#' + to.path; | ||
} | ||
else { | ||
url.hash = (_a = to.hash) !== null && _a !== void 0 ? _a : ''; | ||
} | ||
url.search = to.search | ||
? '?' + new URLSearchParams(to.search).toString() | ||
: ''; | ||
return url.toString(); | ||
} | ||
createLinkHref(to) { | ||
var _a, _b; | ||
const search = (_a = to.search) !== null && _a !== void 0 ? _a : ''; | ||
const hash = to.path != null ? '#' + to.path : (_b = to.hash) !== null && _b !== void 0 ? _b : ''; | ||
return search + hash; | ||
} | ||
replaceParams(to, params) { | ||
const newTo = Object.assign({}, to); | ||
const routeParams = params !== null && params !== void 0 ? params : get_store_value(this.currentRoute).params; | ||
if (newTo.path) { | ||
newTo.path = replacePathParams(newTo.path, routeParams); | ||
} | ||
if (newTo.hash) { | ||
newTo.hash = replacePathParams(newTo.hash, routeParams); | ||
} | ||
return newTo; | ||
} | ||
} | ||
return { | ||
keys: keys, | ||
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i') | ||
}; | ||
} | ||
class PathRouter extends Router { | ||
getCurrentPath() { | ||
return formatPath(window.location.pathname.replace(basePath, '')); | ||
} | ||
getCurrentLocationInput() { | ||
return { | ||
path: this.getCurrentPath(), | ||
hash: window.location.hash, | ||
search: window.location.search | ||
}; | ||
} | ||
getPath(to) { | ||
return to.path; | ||
} | ||
createUrl(to) { | ||
var _a; | ||
const url = new URL(window.location.href); | ||
if (to.path) { | ||
url.pathname = joinPaths(basePath, to.path); | ||
} | ||
url.search = to.search | ||
? '?' + new URLSearchParams(to.search).toString() | ||
: ''; | ||
url.hash = (_a = to.hash) !== null && _a !== void 0 ? _a : ''; | ||
return url.toString(); | ||
} | ||
createLinkHref(to) { | ||
var _a, _b, _c; | ||
const path = (_a = to.path) !== null && _a !== void 0 ? _a : ''; | ||
const search = (_b = to.search) !== null && _b !== void 0 ? _b : ''; | ||
const hash = (_c = to.hash) !== null && _c !== void 0 ? _c : ''; | ||
return joinPaths(basePath, path) + search + hash; | ||
} | ||
replaceParams(to, params) { | ||
const newTo = Object.assign({}, to); | ||
const routeParams = params !== null && params !== void 0 ? params : get_store_value(this.currentRoute).params; | ||
if (newTo.path) { | ||
newTo.path = replacePathParams(newTo.path, routeParams); | ||
} | ||
return newTo; | ||
} | ||
} | ||
class RouteMatcher { | ||
constructor(routes = []) { | ||
this.matchDatas = []; | ||
this.buildDatas(routes); | ||
} | ||
/** Finds a route based on target path and the computed matchers. */ | ||
matchRoute(path) { | ||
// Add trailing slash to route path so it properly matches nested routes too. | ||
// e.g. /foo should match /foo/* | ||
const matchPath = addTrailingSlash(path); | ||
for (const matchData of this.matchDatas) { | ||
const params = {}; | ||
const matchResult = matchPath.match(matchData.pattern); | ||
if (matchResult) { | ||
for (let i = 0; i < matchData.keys.length; i++) { | ||
params[matchData.keys[i]] = matchResult[i + 1]; | ||
} | ||
if ('wild' in params) { | ||
params.wild = formatPath(params.wild); | ||
} | ||
return { | ||
path, | ||
params, | ||
matched: matchData.matched | ||
}; | ||
} | ||
} | ||
} | ||
/** | ||
* Convert the routes as datas that contains information for path matching. | ||
* This will recursively traverse child routes and flatten into `matchDatas`. | ||
*/ | ||
buildDatas(routes, parentData = { path: '', matched: [] }) { | ||
routes.forEach((route) => { | ||
var _a; | ||
// Cumulative metadata when traversing parents | ||
const parent = { | ||
path: joinPaths(parentData.path, route.path), | ||
matched: parentData.matched.concat(route) | ||
}; | ||
if ((_a = route.children) === null || _a === void 0 ? void 0 : _a.length) { | ||
this.buildDatas(route.children, parent); | ||
} | ||
else { | ||
this.matchDatas.push(Object.assign(Object.assign({}, parent), regexparam(parent.path))); | ||
} | ||
}); | ||
// Make sure "/" doesn't fall through if the routes' paths do. | ||
// Example route config: | ||
// "/foo" | ||
// - "/bar" | ||
// match('/foo') should render "/foo" component but with empty child | ||
if (!routes.some((route) => ['', '/', '*', '/*'].includes(route.path))) { | ||
this.matchDatas.push(Object.assign(Object.assign({}, parentData), regexparam(parentData.path))); | ||
} | ||
} | ||
} | ||
// Will be assigned when `initRouter` | ||
exports.navigate = () => { | ||
throw new Error('Router must be initialized before calling navigate'); | ||
}; | ||
// Will be assigned when `initRouter` | ||
exports.createLink = () => { | ||
throw new Error('Router must be initialized before calling createLink'); | ||
}; | ||
const writableRoute = writable({ | ||
path: '', | ||
params: {}, | ||
matched: [], | ||
search: new URLSearchParams(), | ||
hash: '' | ||
}); | ||
const route = { subscribe: writableRoute.subscribe }; | ||
let inited = false; | ||
function initRouter(options) { | ||
if (inited) { | ||
throw new Error('Router is already initialized. Cannot re-initialize router.'); | ||
} | ||
let router; | ||
switch (options.mode) { | ||
case 'hash': | ||
router = new HashRouter(options.routes); | ||
break; | ||
case 'path': | ||
router = new PathRouter(options.routes); | ||
break; | ||
} | ||
exports.navigate = router.navigate.bind(router); | ||
exports.createLink = router.createLink.bind(router); | ||
router.currentRoute.subscribe((v) => writableRoute.set(v)); | ||
inited = true; | ||
} | ||
let routerMode; | ||
let routerHistory; | ||
const writableRoute = writable({ | ||
path: '', | ||
params: {}, | ||
matched: [], | ||
search: new URLSearchParams(), | ||
hash: '' | ||
}); | ||
/** The current route information */ | ||
const route = { subscribe: writableRoute.subscribe }; | ||
/** Initialize the global router. Call this before mounting the app. */ | ||
function initRouter(options) { | ||
var _a; | ||
// Use `routerMode` to check if router is already initialized | ||
if (routerMode == null) { | ||
routerMode = (_a = options.mode) !== null && _a !== void 0 ? _a : 'hash'; | ||
switch (routerMode) { | ||
case 'hash': | ||
routerHistory = new HashHistory(); | ||
break; | ||
case 'history': | ||
routerHistory = new HtmlHistory(); | ||
break; | ||
} | ||
const matcher = new RouteMatcher(options.routes); | ||
routerHistory.currentLocation.subscribe(($currentLocation) => { | ||
var _a, _b; | ||
const matchedRoute = matcher.matchRoute($currentLocation.path); | ||
writableRoute.set(Object.assign(Object.assign({}, $currentLocation), { params: (_a = matchedRoute === null || matchedRoute === void 0 ? void 0 : matchedRoute.params) !== null && _a !== void 0 ? _a : {}, matched: (_b = matchedRoute === null || matchedRoute === void 0 ? void 0 : matchedRoute.matched) !== null && _b !== void 0 ? _b : [] })); | ||
}); | ||
} | ||
} | ||
/* dist/RouterView.svelte generated by Svelte v3.24.0 */ | ||
let routeParams = undefined; | ||
function navigate(to, replace = false) { | ||
if (routerHistory == null) { | ||
// TODO: Show warning if called before initRouter? | ||
return; | ||
} | ||
if (typeof to === 'number') { | ||
routerHistory.go(to); | ||
return; | ||
} | ||
if (typeof to === 'string') { | ||
to = parseLocationInput(to); | ||
} | ||
// Replace, for example, ":id" to `$route.params.id` | ||
to = replaceLocationInputParams(to); | ||
if (replace) { | ||
routerHistory.replace(to); | ||
} | ||
else { | ||
routerHistory.push(to); | ||
} | ||
} | ||
/** Replace named param in path, e.g. `/foo/:id` => `/foo/123` */ | ||
function replacePathParams(path) { | ||
if (routeParams == null) { | ||
route.subscribe(($route) => { | ||
routeParams = $route.params; | ||
}); | ||
} | ||
return path.replace(/:([^/]+)/g, (o, v) => { var _a; return (_a = routeParams[v]) !== null && _a !== void 0 ? _a : o; }); | ||
} | ||
function replaceLocationInputParams(input) { | ||
const newInput = Object.assign({}, input); | ||
if (newInput.path) { | ||
newInput.path = replacePathParams(newInput.path); | ||
} | ||
if (routerMode === 'hash' && newInput.hash) { | ||
newInput.hash = replacePathParams(newInput.hash); | ||
} | ||
return newInput; | ||
} | ||
function parseLocationInput(to) { | ||
const url = new URL(to, 'https://example.com'); | ||
const hasPath = to.length > 0 && !to.startsWith('?') && !to.startsWith('#'); | ||
const path = hasPath ? url.pathname : undefined; | ||
const search = url.search !== '' ? url.search : undefined; | ||
const hash = url.hash !== '' ? url.hash : undefined; | ||
return { path, search, hash }; | ||
} | ||
function create_if_block(ctx) { | ||
let current_block_type_index; | ||
let if_block; | ||
let if_block_anchor; | ||
let current; | ||
const if_block_creators = [create_if_block_1, create_if_block_3]; | ||
const if_blocks = []; | ||
/* dist/RouterView.svelte generated by Svelte v3.24.0 */ | ||
function select_block_type(ctx, dirty) { | ||
if (/*component*/ ctx[3] != null) return 0; | ||
if (/*hasChildren*/ ctx[2]) return 1; | ||
return -1; | ||
} | ||
function create_if_block(ctx) { | ||
let current_block_type_index; | ||
let if_block; | ||
let if_block_anchor; | ||
let current; | ||
const if_block_creators = [create_if_block_1, create_if_block_3]; | ||
const if_blocks = []; | ||
if (~(current_block_type_index = select_block_type(ctx))) { | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
} | ||
function select_block_type(ctx, dirty) { | ||
if (/*component*/ ctx[3] != null) return 0; | ||
if (/*hasChildren*/ ctx[2]) return 1; | ||
return -1; | ||
} | ||
return { | ||
c() { | ||
if (if_block) if_block.c(); | ||
if_block_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (~current_block_type_index) { | ||
if_blocks[current_block_type_index].m(target, anchor); | ||
} | ||
if (~(current_block_type_index = select_block_type(ctx))) { | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
} | ||
insert(target, if_block_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
let previous_block_index = current_block_type_index; | ||
current_block_type_index = select_block_type(ctx); | ||
return { | ||
c() { | ||
if (if_block) if_block.c(); | ||
if_block_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (~current_block_type_index) { | ||
if_blocks[current_block_type_index].m(target, anchor); | ||
} | ||
if (current_block_type_index === previous_block_index) { | ||
if (~current_block_type_index) { | ||
if_blocks[current_block_type_index].p(ctx, dirty); | ||
} | ||
} else { | ||
if (if_block) { | ||
group_outros(); | ||
insert(target, if_block_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
let previous_block_index = current_block_type_index; | ||
current_block_type_index = select_block_type(ctx); | ||
transition_out(if_blocks[previous_block_index], 1, 1, () => { | ||
if_blocks[previous_block_index] = null; | ||
}); | ||
if (current_block_type_index === previous_block_index) { | ||
if (~current_block_type_index) { | ||
if_blocks[current_block_type_index].p(ctx, dirty); | ||
} | ||
} else { | ||
if (if_block) { | ||
group_outros(); | ||
check_outros(); | ||
} | ||
transition_out(if_blocks[previous_block_index], 1, 1, () => { | ||
if_blocks[previous_block_index] = null; | ||
}); | ||
if (~current_block_type_index) { | ||
if_block = if_blocks[current_block_type_index]; | ||
check_outros(); | ||
} | ||
if (!if_block) { | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
if_block.c(); | ||
} | ||
if (~current_block_type_index) { | ||
if_block = if_blocks[current_block_type_index]; | ||
transition_in(if_block, 1); | ||
if_block.m(if_block_anchor.parentNode, if_block_anchor); | ||
} else { | ||
if_block = null; | ||
} | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(if_block); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(if_block); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (~current_block_type_index) { | ||
if_blocks[current_block_type_index].d(detaching); | ||
} | ||
if (!if_block) { | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
if_block.c(); | ||
} | ||
if (detaching) detach(if_block_anchor); | ||
} | ||
}; | ||
} | ||
transition_in(if_block, 1); | ||
if_block.m(if_block_anchor.parentNode, if_block_anchor); | ||
} else { | ||
if_block = null; | ||
} | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(if_block); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(if_block); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (~current_block_type_index) { | ||
if_blocks[current_block_type_index].d(detaching); | ||
} | ||
// (34:24) | ||
function create_if_block_3(ctx) { | ||
let routerview; | ||
let current; | ||
if (detaching) detach(if_block_anchor); | ||
} | ||
}; | ||
} | ||
routerview = new RouterView({ | ||
props: { nextMatched: /*matched*/ ctx[1].slice(1) } | ||
}); | ||
// (34:24) | ||
function create_if_block_3(ctx) { | ||
let routerview; | ||
let current; | ||
return { | ||
c() { | ||
create_component(routerview.$$.fragment); | ||
}, | ||
m(target, anchor) { | ||
mount_component(routerview, target, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
const routerview_changes = {}; | ||
if (dirty & /*matched*/ 2) routerview_changes.nextMatched = /*matched*/ ctx[1].slice(1); | ||
routerview.$set(routerview_changes); | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(routerview.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(routerview.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
destroy_component(routerview, detaching); | ||
} | ||
}; | ||
} | ||
routerview = new RouterView({ | ||
props: { nextMatched: /*matched*/ ctx[1].slice(1) } | ||
}); | ||
// (26:2) {#if component != null} | ||
function create_if_block_1(ctx) { | ||
let current_block_type_index; | ||
let if_block; | ||
let if_block_anchor; | ||
let current; | ||
const if_block_creators = [create_if_block_2, create_else_block]; | ||
const if_blocks = []; | ||
return { | ||
c() { | ||
create_component(routerview.$$.fragment); | ||
}, | ||
m(target, anchor) { | ||
mount_component(routerview, target, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
const routerview_changes = {}; | ||
if (dirty & /*matched*/ 2) routerview_changes.nextMatched = /*matched*/ ctx[1].slice(1); | ||
routerview.$set(routerview_changes); | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(routerview.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(routerview.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
destroy_component(routerview, detaching); | ||
} | ||
}; | ||
} | ||
function select_block_type_1(ctx, dirty) { | ||
if (/*hasChildren*/ ctx[2]) return 0; | ||
return 1; | ||
} | ||
// (26:2) {#if component != null} | ||
function create_if_block_1(ctx) { | ||
let current_block_type_index; | ||
let if_block; | ||
let if_block_anchor; | ||
let current; | ||
const if_block_creators = [create_if_block_2, create_else_block]; | ||
const if_blocks = []; | ||
current_block_type_index = select_block_type_1(ctx); | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
function select_block_type_1(ctx, dirty) { | ||
if (/*hasChildren*/ ctx[2]) return 0; | ||
return 1; | ||
} | ||
return { | ||
c() { | ||
if_block.c(); | ||
if_block_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if_blocks[current_block_type_index].m(target, anchor); | ||
insert(target, if_block_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
let previous_block_index = current_block_type_index; | ||
current_block_type_index = select_block_type_1(ctx); | ||
current_block_type_index = select_block_type_1(ctx); | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
if (current_block_type_index === previous_block_index) { | ||
if_blocks[current_block_type_index].p(ctx, dirty); | ||
} else { | ||
group_outros(); | ||
return { | ||
c() { | ||
if_block.c(); | ||
if_block_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if_blocks[current_block_type_index].m(target, anchor); | ||
insert(target, if_block_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
let previous_block_index = current_block_type_index; | ||
current_block_type_index = select_block_type_1(ctx); | ||
transition_out(if_blocks[previous_block_index], 1, 1, () => { | ||
if_blocks[previous_block_index] = null; | ||
}); | ||
if (current_block_type_index === previous_block_index) { | ||
if_blocks[current_block_type_index].p(ctx, dirty); | ||
} else { | ||
group_outros(); | ||
check_outros(); | ||
if_block = if_blocks[current_block_type_index]; | ||
transition_out(if_blocks[previous_block_index], 1, 1, () => { | ||
if_blocks[previous_block_index] = null; | ||
}); | ||
if (!if_block) { | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
if_block.c(); | ||
} | ||
check_outros(); | ||
if_block = if_blocks[current_block_type_index]; | ||
transition_in(if_block, 1); | ||
if_block.m(if_block_anchor.parentNode, if_block_anchor); | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(if_block); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(if_block); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if_blocks[current_block_type_index].d(detaching); | ||
if (detaching) detach(if_block_anchor); | ||
} | ||
}; | ||
} | ||
if (!if_block) { | ||
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); | ||
if_block.c(); | ||
} | ||
// (31:4) {:else} | ||
function create_else_block(ctx) { | ||
let switch_instance; | ||
let switch_instance_anchor; | ||
let current; | ||
var switch_value = /*component*/ ctx[3]; | ||
transition_in(if_block, 1); | ||
if_block.m(if_block_anchor.parentNode, if_block_anchor); | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(if_block); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(if_block); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if_blocks[current_block_type_index].d(detaching); | ||
if (detaching) detach(if_block_anchor); | ||
} | ||
}; | ||
} | ||
function switch_props(ctx) { | ||
return {}; | ||
} | ||
// (31:4) {:else} | ||
function create_else_block(ctx) { | ||
let switch_instance; | ||
let switch_instance_anchor; | ||
let current; | ||
var switch_value = /*component*/ ctx[3]; | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props()); | ||
} | ||
function switch_props(ctx) { | ||
return {}; | ||
} | ||
return { | ||
c() { | ||
if (switch_instance) create_component(switch_instance.$$.fragment); | ||
switch_instance_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (switch_instance) { | ||
mount_component(switch_instance, target, anchor); | ||
} | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props()); | ||
} | ||
insert(target, switch_instance_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
if (switch_value !== (switch_value = /*component*/ ctx[3])) { | ||
if (switch_instance) { | ||
group_outros(); | ||
const old_component = switch_instance; | ||
return { | ||
c() { | ||
if (switch_instance) create_component(switch_instance.$$.fragment); | ||
switch_instance_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (switch_instance) { | ||
mount_component(switch_instance, target, anchor); | ||
} | ||
transition_out(old_component.$$.fragment, 1, 0, () => { | ||
destroy_component(old_component, 1); | ||
}); | ||
insert(target, switch_instance_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
if (switch_value !== (switch_value = /*component*/ ctx[3])) { | ||
if (switch_instance) { | ||
group_outros(); | ||
const old_component = switch_instance; | ||
check_outros(); | ||
} | ||
transition_out(old_component.$$.fragment, 1, 0, () => { | ||
destroy_component(old_component, 1); | ||
}); | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props()); | ||
create_component(switch_instance.$$.fragment); | ||
transition_in(switch_instance.$$.fragment, 1); | ||
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor); | ||
} else { | ||
switch_instance = null; | ||
} | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
if (switch_instance) transition_in(switch_instance.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
if (switch_instance) transition_out(switch_instance.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (detaching) detach(switch_instance_anchor); | ||
if (switch_instance) destroy_component(switch_instance, detaching); | ||
} | ||
}; | ||
} | ||
check_outros(); | ||
} | ||
// (27:4) {#if hasChildren} | ||
function create_if_block_2(ctx) { | ||
let switch_instance; | ||
let switch_instance_anchor; | ||
let current; | ||
var switch_value = /*component*/ ctx[3]; | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props()); | ||
create_component(switch_instance.$$.fragment); | ||
transition_in(switch_instance.$$.fragment, 1); | ||
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor); | ||
} else { | ||
switch_instance = null; | ||
} | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
if (switch_instance) transition_in(switch_instance.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
if (switch_instance) transition_out(switch_instance.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (detaching) detach(switch_instance_anchor); | ||
if (switch_instance) destroy_component(switch_instance, detaching); | ||
} | ||
}; | ||
} | ||
function switch_props(ctx) { | ||
return { | ||
props: { | ||
$$slots: { default: [create_default_slot] }, | ||
$$scope: { ctx } | ||
} | ||
}; | ||
} | ||
// (27:4) {#if hasChildren} | ||
function create_if_block_2(ctx) { | ||
let switch_instance; | ||
let switch_instance_anchor; | ||
let current; | ||
var switch_value = /*component*/ ctx[3]; | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props(ctx)); | ||
} | ||
function switch_props(ctx) { | ||
return { | ||
props: { | ||
$$slots: { default: [create_default_slot] }, | ||
$$scope: { ctx } | ||
} | ||
}; | ||
} | ||
return { | ||
c() { | ||
if (switch_instance) create_component(switch_instance.$$.fragment); | ||
switch_instance_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (switch_instance) { | ||
mount_component(switch_instance, target, anchor); | ||
} | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props(ctx)); | ||
} | ||
insert(target, switch_instance_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
const switch_instance_changes = {}; | ||
return { | ||
c() { | ||
if (switch_instance) create_component(switch_instance.$$.fragment); | ||
switch_instance_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (switch_instance) { | ||
mount_component(switch_instance, target, anchor); | ||
} | ||
if (dirty & /*$$scope, matched*/ 514) { | ||
switch_instance_changes.$$scope = { dirty, ctx }; | ||
} | ||
insert(target, switch_instance_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
const switch_instance_changes = {}; | ||
if (switch_value !== (switch_value = /*component*/ ctx[3])) { | ||
if (switch_instance) { | ||
group_outros(); | ||
const old_component = switch_instance; | ||
if (dirty & /*$$scope, matched*/ 514) { | ||
switch_instance_changes.$$scope = { dirty, ctx }; | ||
} | ||
transition_out(old_component.$$.fragment, 1, 0, () => { | ||
destroy_component(old_component, 1); | ||
}); | ||
if (switch_value !== (switch_value = /*component*/ ctx[3])) { | ||
if (switch_instance) { | ||
group_outros(); | ||
const old_component = switch_instance; | ||
check_outros(); | ||
} | ||
transition_out(old_component.$$.fragment, 1, 0, () => { | ||
destroy_component(old_component, 1); | ||
}); | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props(ctx)); | ||
create_component(switch_instance.$$.fragment); | ||
transition_in(switch_instance.$$.fragment, 1); | ||
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor); | ||
} else { | ||
switch_instance = null; | ||
} | ||
} else if (switch_value) { | ||
switch_instance.$set(switch_instance_changes); | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
if (switch_instance) transition_in(switch_instance.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
if (switch_instance) transition_out(switch_instance.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (detaching) detach(switch_instance_anchor); | ||
if (switch_instance) destroy_component(switch_instance, detaching); | ||
} | ||
}; | ||
} | ||
check_outros(); | ||
} | ||
// (28:6) <svelte:component this={component}> | ||
function create_default_slot(ctx) { | ||
let routerview; | ||
let current; | ||
if (switch_value) { | ||
switch_instance = new switch_value(switch_props(ctx)); | ||
create_component(switch_instance.$$.fragment); | ||
transition_in(switch_instance.$$.fragment, 1); | ||
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor); | ||
} else { | ||
switch_instance = null; | ||
} | ||
} else if (switch_value) { | ||
switch_instance.$set(switch_instance_changes); | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
if (switch_instance) transition_in(switch_instance.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
if (switch_instance) transition_out(switch_instance.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (detaching) detach(switch_instance_anchor); | ||
if (switch_instance) destroy_component(switch_instance, detaching); | ||
} | ||
}; | ||
} | ||
routerview = new RouterView({ | ||
props: { nextMatched: /*matched*/ ctx[1].slice(1) } | ||
}); | ||
// (28:6) <svelte:component this={component}> | ||
function create_default_slot(ctx) { | ||
let routerview; | ||
let current; | ||
return { | ||
c() { | ||
create_component(routerview.$$.fragment); | ||
}, | ||
m(target, anchor) { | ||
mount_component(routerview, target, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
const routerview_changes = {}; | ||
if (dirty & /*matched*/ 2) routerview_changes.nextMatched = /*matched*/ ctx[1].slice(1); | ||
routerview.$set(routerview_changes); | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(routerview.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(routerview.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
destroy_component(routerview, detaching); | ||
} | ||
}; | ||
} | ||
routerview = new RouterView({ | ||
props: { nextMatched: /*matched*/ ctx[1].slice(1) } | ||
}); | ||
function create_fragment(ctx) { | ||
let if_block_anchor; | ||
let current; | ||
let if_block = (!/*redirect*/ ctx[4] || /*canRender*/ ctx[0]) && create_if_block(ctx); | ||
return { | ||
c() { | ||
create_component(routerview.$$.fragment); | ||
}, | ||
m(target, anchor) { | ||
mount_component(routerview, target, anchor); | ||
current = true; | ||
}, | ||
p(ctx, dirty) { | ||
const routerview_changes = {}; | ||
if (dirty & /*matched*/ 2) routerview_changes.nextMatched = /*matched*/ ctx[1].slice(1); | ||
routerview.$set(routerview_changes); | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(routerview.$$.fragment, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(routerview.$$.fragment, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
destroy_component(routerview, detaching); | ||
} | ||
}; | ||
} | ||
return { | ||
c() { | ||
if (if_block) if_block.c(); | ||
if_block_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (if_block) if_block.m(target, anchor); | ||
insert(target, if_block_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, [dirty]) { | ||
if (!/*redirect*/ ctx[4] || /*canRender*/ ctx[0]) { | ||
if (if_block) { | ||
if_block.p(ctx, dirty); | ||
function create_fragment(ctx) { | ||
let if_block_anchor; | ||
let current; | ||
let if_block = (!/*redirect*/ ctx[4] || /*canRender*/ ctx[0]) && create_if_block(ctx); | ||
if (dirty & /*redirect, canRender*/ 17) { | ||
transition_in(if_block, 1); | ||
} | ||
} else { | ||
if_block = create_if_block(ctx); | ||
if_block.c(); | ||
transition_in(if_block, 1); | ||
if_block.m(if_block_anchor.parentNode, if_block_anchor); | ||
} | ||
} else if (if_block) { | ||
group_outros(); | ||
return { | ||
c() { | ||
if (if_block) if_block.c(); | ||
if_block_anchor = empty(); | ||
}, | ||
m(target, anchor) { | ||
if (if_block) if_block.m(target, anchor); | ||
insert(target, if_block_anchor, anchor); | ||
current = true; | ||
}, | ||
p(ctx, [dirty]) { | ||
if (!/*redirect*/ ctx[4] || /*canRender*/ ctx[0]) { | ||
if (if_block) { | ||
if_block.p(ctx, dirty); | ||
transition_out(if_block, 1, 1, () => { | ||
if_block = null; | ||
}); | ||
if (dirty & /*redirect, canRender*/ 17) { | ||
transition_in(if_block, 1); | ||
} | ||
} else { | ||
if_block = create_if_block(ctx); | ||
if_block.c(); | ||
transition_in(if_block, 1); | ||
if_block.m(if_block_anchor.parentNode, if_block_anchor); | ||
} | ||
} else if (if_block) { | ||
group_outros(); | ||
check_outros(); | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(if_block); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(if_block); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (if_block) if_block.d(detaching); | ||
if (detaching) detach(if_block_anchor); | ||
} | ||
}; | ||
} | ||
transition_out(if_block, 1, 1, () => { | ||
if_block = null; | ||
}); | ||
function instance($$self, $$props, $$invalidate) { | ||
let $route; | ||
component_subscribe($$self, route, $$value => $$invalidate(8, $route = $$value)); | ||
var _a, _b; | ||
let { nextMatched = undefined } = $$props; | ||
let canRender = false; | ||
check_outros(); | ||
} | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(if_block); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(if_block); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (if_block) if_block.d(detaching); | ||
if (detaching) detach(if_block_anchor); | ||
} | ||
}; | ||
} | ||
$$self.$set = $$props => { | ||
if ("nextMatched" in $$props) $$invalidate(5, nextMatched = $$props.nextMatched); | ||
}; | ||
function instance($$self, $$props, $$invalidate) { | ||
let $route; | ||
component_subscribe($$self, route, $$value => $$invalidate(8, $route = $$value)); | ||
var _a, _b; | ||
let { nextMatched = undefined } = $$props; | ||
let canRender = false; | ||
let matched; | ||
let hasChildren; | ||
let component; | ||
let redirect; | ||
$$self.$set = $$props => { | ||
if ("nextMatched" in $$props) $$invalidate(5, nextMatched = $$props.nextMatched); | ||
}; | ||
$$self.$$.update = () => { | ||
if ($$self.$$.dirty & /*nextMatched, $route*/ 288) { | ||
$$invalidate(1, matched = nextMatched !== null && nextMatched !== void 0 | ||
? nextMatched | ||
: $route.matched); | ||
} | ||
let matched; | ||
let hasChildren; | ||
let component; | ||
let redirect; | ||
if ($$self.$$.dirty & /*matched*/ 2) { | ||
$$invalidate(2, hasChildren = matched.length > 1); | ||
} | ||
$$self.$$.update = () => { | ||
if ($$self.$$.dirty & /*nextMatched, $route*/ 288) { | ||
$$invalidate(1, matched = nextMatched !== null && nextMatched !== void 0 | ||
? nextMatched | ||
: $route.matched); | ||
} | ||
if ($$self.$$.dirty & /*matched, _a*/ 66) { | ||
$$invalidate(3, component = $$invalidate(6, _a = matched[0]) === null || _a === void 0 | ||
? void 0 | ||
: _a.component); | ||
} | ||
if ($$self.$$.dirty & /*matched*/ 2) { | ||
$$invalidate(2, hasChildren = matched.length > 1); | ||
} | ||
if ($$self.$$.dirty & /*matched, _b*/ 130) { | ||
$$invalidate(4, redirect = $$invalidate(7, _b = matched[0]) === null || _b === void 0 | ||
? void 0 | ||
: _b.redirect); | ||
} | ||
if ($$self.$$.dirty & /*matched, _a*/ 66) { | ||
$$invalidate(3, component = $$invalidate(6, _a = matched[0]) === null || _a === void 0 | ||
? void 0 | ||
: _a.component); | ||
} | ||
if ($$self.$$.dirty & /*redirect*/ 16) { | ||
if (redirect != null) { | ||
$$invalidate(0, canRender = false); | ||
if ($$self.$$.dirty & /*matched, _b*/ 130) { | ||
$$invalidate(4, redirect = $$invalidate(7, _b = matched[0]) === null || _b === void 0 | ||
? void 0 | ||
: _b.redirect); | ||
} | ||
handlePromisable(handleThunk(redirect), result => { | ||
if (result != null) { | ||
tick().then(() => exports.navigate(result, true)); | ||
} else { | ||
$$invalidate(0, canRender = true); | ||
} | ||
}); | ||
} | ||
} | ||
}; | ||
if ($$self.$$.dirty & /*redirect*/ 16) { | ||
if (redirect != null) { | ||
$$invalidate(0, canRender = false); | ||
return [canRender, matched, hasChildren, component, redirect, nextMatched]; | ||
} | ||
handlePromisable(handleThunk(redirect), result => { | ||
if (result != null) { | ||
navigate(result, true); | ||
} else { | ||
$$invalidate(0, canRender = true); | ||
} | ||
}); | ||
} | ||
} | ||
}; | ||
class RouterView extends SvelteComponent { | ||
constructor(options) { | ||
super(); | ||
init(this, options, instance, create_fragment, safe_not_equal, { nextMatched: 5 }); | ||
} | ||
} | ||
return [canRender, matched, hasChildren, component, redirect, nextMatched]; | ||
} | ||
/* dist/Link.svelte generated by Svelte v3.24.0 */ | ||
class RouterView extends SvelteComponent { | ||
constructor(options) { | ||
super(); | ||
init(this, options, instance, create_fragment, safe_not_equal, { nextMatched: 5 }); | ||
} | ||
} | ||
function create_fragment$1(ctx) { | ||
let a; | ||
let a_href_value; | ||
let a_aria_current_value; | ||
let current; | ||
let mounted; | ||
let dispose; | ||
const default_slot_template = /*$$slots*/ ctx[6].default; | ||
const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[5], null); | ||
/* dist/Link.svelte generated by Svelte v3.24.0 */ | ||
let a_levels = [ | ||
/*$$restProps*/ ctx[4], | ||
{ | ||
href: a_href_value = /*$link*/ ctx[3].href | ||
}, | ||
{ | ||
"aria-current": a_aria_current_value = /*$link*/ ctx[3].isExactActive ? "page" : undefined | ||
} | ||
]; | ||
function create_fragment$1(ctx) { | ||
let a; | ||
let a_aria_current_value; | ||
let current; | ||
let mounted; | ||
let dispose; | ||
const default_slot_template = /*$$slots*/ ctx[7].default; | ||
const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[6], null); | ||
let a_data = {}; | ||
let a_levels = [ | ||
/*$$restProps*/ ctx[5], | ||
{ href: /*href*/ ctx[2] }, | ||
{ | ||
"aria-current": a_aria_current_value = /*isExactActive*/ ctx[4] ? "page" : undefined | ||
} | ||
]; | ||
for (let i = 0; i < a_levels.length; i += 1) { | ||
a_data = assign(a_data, a_levels[i]); | ||
} | ||
let a_data = {}; | ||
return { | ||
c() { | ||
a = element("a"); | ||
if (default_slot) default_slot.c(); | ||
set_attributes(a, a_data); | ||
toggle_class(a, "link-active", /*$link*/ ctx[3].isActive); | ||
toggle_class(a, "link-exact-active", /*$link*/ ctx[3].isExactActive); | ||
}, | ||
m(target, anchor) { | ||
insert(target, a, anchor); | ||
for (let i = 0; i < a_levels.length; i += 1) { | ||
a_data = assign(a_data, a_levels[i]); | ||
} | ||
if (default_slot) { | ||
default_slot.m(a, null); | ||
} | ||
return { | ||
c() { | ||
a = element("a"); | ||
if (default_slot) default_slot.c(); | ||
set_attributes(a, a_data); | ||
toggle_class(a, "link-active", /*isActive*/ ctx[3]); | ||
toggle_class(a, "link-exact-active", /*isExactActive*/ ctx[4]); | ||
}, | ||
m(target, anchor) { | ||
insert(target, a, anchor); | ||
current = true; | ||
if (default_slot) { | ||
default_slot.m(a, null); | ||
} | ||
if (!mounted) { | ||
dispose = listen(a, "click", prevent_default(/*click_handler*/ ctx[7])); | ||
mounted = true; | ||
} | ||
}, | ||
p(ctx, [dirty]) { | ||
if (default_slot) { | ||
if (default_slot.p && dirty & /*$$scope*/ 32) { | ||
update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[5], dirty, null, null); | ||
} | ||
} | ||
current = true; | ||
set_attributes(a, a_data = get_spread_update(a_levels, [ | ||
dirty & /*$$restProps*/ 16 && /*$$restProps*/ ctx[4], | ||
(!current || dirty & /*$link*/ 8 && a_href_value !== (a_href_value = /*$link*/ ctx[3].href)) && { href: a_href_value }, | ||
(!current || dirty & /*$link*/ 8 && a_aria_current_value !== (a_aria_current_value = /*$link*/ ctx[3].isExactActive ? "page" : undefined)) && { "aria-current": a_aria_current_value } | ||
])); | ||
if (!mounted) { | ||
dispose = listen(a, "click", prevent_default(/*click_handler*/ ctx[8])); | ||
mounted = true; | ||
} | ||
}, | ||
p(ctx, [dirty]) { | ||
if (default_slot) { | ||
if (default_slot.p && dirty & /*$$scope*/ 64) { | ||
update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[6], dirty, null, null); | ||
} | ||
} | ||
toggle_class(a, "link-active", /*$link*/ ctx[3].isActive); | ||
toggle_class(a, "link-exact-active", /*$link*/ ctx[3].isExactActive); | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(default_slot, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(default_slot, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (detaching) detach(a); | ||
if (default_slot) default_slot.d(detaching); | ||
mounted = false; | ||
dispose(); | ||
} | ||
}; | ||
} | ||
set_attributes(a, a_data = get_spread_update(a_levels, [ | ||
dirty & /*$$restProps*/ 32 && /*$$restProps*/ ctx[5], | ||
(!current || dirty & /*href*/ 4) && { href: /*href*/ ctx[2] }, | ||
(!current || dirty & /*isExactActive*/ 16 && a_aria_current_value !== (a_aria_current_value = /*isExactActive*/ ctx[4] ? "page" : undefined)) && { "aria-current": a_aria_current_value } | ||
])); | ||
function instance$1($$self, $$props, $$invalidate) { | ||
const omit_props_names = ["to","replace"]; | ||
let $$restProps = compute_rest_props($$props, omit_props_names); | ||
toggle_class(a, "link-active", /*isActive*/ ctx[3]); | ||
toggle_class(a, "link-exact-active", /*isExactActive*/ ctx[4]); | ||
}, | ||
i(local) { | ||
if (current) return; | ||
transition_in(default_slot, local); | ||
current = true; | ||
}, | ||
o(local) { | ||
transition_out(default_slot, local); | ||
current = false; | ||
}, | ||
d(detaching) { | ||
if (detaching) detach(a); | ||
if (default_slot) default_slot.d(detaching); | ||
mounted = false; | ||
dispose(); | ||
} | ||
}; | ||
} | ||
let $link, | ||
$$unsubscribe_link = noop, | ||
$$subscribe_link = () => ($$unsubscribe_link(), $$unsubscribe_link = subscribe(link, $$value => $$invalidate(3, $link = $$value)), link); | ||
function instance$1($$self, $$props, $$invalidate) { | ||
const omit_props_names = ["to","replace"]; | ||
let $$restProps = compute_rest_props($$props, omit_props_names); | ||
let $route; | ||
component_subscribe($$self, route, $$value => $$invalidate(17, $route = $$value)); | ||
var _a, _b, _c, _d; | ||
let { to } = $$props; | ||
let { replace = false } = $$props; | ||
$$self.$$.on_destroy.push(() => $$unsubscribe_link()); | ||
let { to } = $$props; | ||
let { replace = false } = $$props; | ||
let { $$slots = {}, $$scope } = $$props; | ||
const click_handler = () => exports.navigate(to, replace); | ||
// The input path only, no search and hash | ||
let path; | ||
$$self.$set = $$new_props => { | ||
$$props = assign(assign({}, $$props), exclude_internal_props($$new_props)); | ||
$$invalidate(4, $$restProps = compute_rest_props($$props, omit_props_names)); | ||
if ("to" in $$new_props) $$invalidate(0, to = $$new_props.to); | ||
if ("replace" in $$new_props) $$invalidate(1, replace = $$new_props.replace); | ||
if ("$$scope" in $$new_props) $$invalidate(5, $$scope = $$new_props.$$scope); | ||
}; | ||
let href; | ||
let { $$slots = {}, $$scope } = $$props; | ||
const click_handler = () => navigate(to, replace); | ||
let link; | ||
$$self.$set = $$new_props => { | ||
$$props = assign(assign({}, $$props), exclude_internal_props($$new_props)); | ||
$$invalidate(5, $$restProps = compute_rest_props($$props, omit_props_names)); | ||
if ("to" in $$new_props) $$invalidate(0, to = $$new_props.to); | ||
if ("replace" in $$new_props) $$invalidate(1, replace = $$new_props.replace); | ||
if ("$$scope" in $$new_props) $$invalidate(6, $$scope = $$new_props.$$scope); | ||
}; | ||
$$self.$$.update = () => { | ||
if ($$self.$$.dirty & /*to*/ 1) { | ||
$$subscribe_link($$invalidate(2, link = exports.createLink(to))); | ||
} | ||
}; | ||
let input; | ||
let parsedInput; | ||
let formattedPath; | ||
let isActive; | ||
let isExactActive; | ||
return [to, replace, link, $link, $$restProps, $$scope, $$slots, click_handler]; | ||
} | ||
$$self.$$.update = () => { | ||
if ($$self.$$.dirty & /*to*/ 1) { | ||
$$invalidate(14, input = typeof to === "string" ? parseLocationInput(to) : to); | ||
} | ||
class Link extends SvelteComponent { | ||
constructor(options) { | ||
super(); | ||
init(this, options, instance$1, create_fragment$1, safe_not_equal, { to: 0, replace: 1 }); | ||
} | ||
} | ||
if ($$self.$$.dirty & /*input*/ 16384) { | ||
$$invalidate(15, parsedInput = replaceLocationInputParams(input)); | ||
} | ||
exports.Link = Link; | ||
exports.RouterView = RouterView; | ||
exports.initRouter = initRouter; | ||
exports.route = route; | ||
if ($$self.$$.dirty & /*parsedInput, _a, _b*/ 34304) { | ||
switch (routerMode) { | ||
case "hash": | ||
$$invalidate(13, path = $$invalidate(9, _a = parsedInput.path) !== null && _a !== void 0 | ||
? _a | ||
: $$invalidate(10, _b = parsedInput.hash) === null || _b === void 0 | ||
? void 0 | ||
: _b.slice(1)); | ||
break; | ||
case "history": | ||
$$invalidate(13, path = parsedInput.path); | ||
break; | ||
} | ||
} | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
if ($$self.$$.dirty & /*path*/ 8192) { | ||
$$invalidate(16, formattedPath = path != null ? formatPath(path) : undefined); | ||
} | ||
if ($$self.$$.dirty & /*parsedInput, _c, path, _d*/ 47104) { | ||
{ | ||
const searchString = $$invalidate(11, _c = parsedInput.search) === null || _c === void 0 | ||
? void 0 | ||
: _c.toString(); | ||
const search = searchString ? "?" + searchString : ""; | ||
switch (routerMode) { | ||
case "hash": | ||
$$invalidate(2, href = search + (path != null ? "#" + path : "")); | ||
break; | ||
case "history": | ||
$$invalidate(2, href = joinPaths(basePath, (path !== null && path !== void 0 ? path : "") + search + ($$invalidate(12, _d = parsedInput.hash) !== null && _d !== void 0 | ||
? _d | ||
: ""))); | ||
break; | ||
} | ||
} | ||
} | ||
if ($$self.$$.dirty & /*formattedPath, $route*/ 196608) { | ||
// Partial path match | ||
$$invalidate(3, isActive = formattedPath && $route.path.startsWith(formattedPath)); | ||
} | ||
if ($$self.$$.dirty & /*$route, formattedPath*/ 196608) { | ||
// Exact path match | ||
$$invalidate(4, isExactActive = $route.path === formattedPath); | ||
} | ||
}; | ||
return [ | ||
to, | ||
replace, | ||
href, | ||
isActive, | ||
isExactActive, | ||
$$restProps, | ||
$$scope, | ||
$$slots, | ||
click_handler | ||
]; | ||
} | ||
class Link extends SvelteComponent { | ||
constructor(options) { | ||
super(); | ||
init(this, options, instance$1, create_fragment$1, safe_not_equal, { to: 0, replace: 1 }); | ||
} | ||
} | ||
/** | ||
* Use this action on an anchor tag to automatically handle router navigation. | ||
* Can also be used on any element so its decendants' anchor tags are handled | ||
* as well. | ||
* | ||
* Only href that starts with "/", "?" or "#" will be routed. If a href matches | ||
* this pattern but doesn't need routing, add a `noroute` attribute to the | ||
* anchor tag. | ||
* | ||
* The navigation will `push` by default. Add a `replace` attribute on the | ||
* anchor tag to `replace`. | ||
*/ | ||
function link(node) { | ||
node.addEventListener('click', handleClick); | ||
return { | ||
destroy: () => node.removeEventListener('click', handleClick) | ||
}; | ||
} | ||
function handleClick(e) { | ||
if (e.ctrlKey || | ||
e.metaKey || | ||
e.altKey || | ||
e.shiftKey || | ||
e.button || | ||
e.defaultPrevented) { | ||
return; | ||
} | ||
const a = e.target.closest('a'); | ||
if (a != null) { | ||
const href = a.getAttribute('href'); | ||
if (href != null && | ||
(href.startsWith('/') || href.startsWith('?') || href.startsWith('#')) && | ||
!isAttributeTrue(a, 'noroute')) { | ||
e.preventDefault(); | ||
navigate(href, isAttributeTrue(a, 'replace')); | ||
} | ||
} | ||
} | ||
exports.Link = Link; | ||
exports.RouterView = RouterView; | ||
exports.initRouter = initRouter; | ||
exports.link = link; | ||
exports.navigate = navigate; | ||
exports.route = route; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); |
export declare type Thunk<T> = T | (() => T); | ||
export declare type Promisable<T> = T | Promise<T>; | ||
export declare type RouterMode = 'hash' | 'history'; | ||
export interface LocationInput { | ||
@@ -15,2 +14,3 @@ path?: string; | ||
component?: any; | ||
/** Redirect to another path if route match */ | ||
redirect?: RedirectOption; | ||
@@ -17,0 +17,0 @@ /** |
@@ -1,2 +0,6 @@ | ||
import { Thunk, Promisable } from './types'; | ||
import { Thunk, Promisable, LocationInput } from './types'; | ||
export declare const basePath: string; | ||
export declare function parseLocationInput(to: string): LocationInput; | ||
/** Replace named param in path, e.g. `/foo/:id` => `/foo/123` */ | ||
export declare function replacePathParams(path: string, params: Record<string, string>): string; | ||
/** Makes sure path has leading "/" and no trailing "/" */ | ||
@@ -9,3 +13,1 @@ export declare function formatPath(path: string): string; | ||
export declare function handlePromisable<T>(promisable: Promisable<T>, callback: (value: T) => void): void; | ||
/** Attribute is true only if it's not null and value not "false" */ | ||
export declare function isAttributeTrue(el: Element, attrName: string): boolean; |
@@ -0,1 +1,16 @@ | ||
export const basePath = document.getElementsByTagName('base').length > 0 | ||
? document.baseURI.replace(window.location.origin, '') | ||
: '/'; | ||
export function parseLocationInput(to) { | ||
const url = new URL(to, 'https://example.com'); | ||
return { | ||
path: to.startsWith('/') ? url.pathname : undefined, | ||
search: url.search, | ||
hash: url.hash | ||
}; | ||
} | ||
/** Replace named param in path, e.g. `/foo/:id` => `/foo/123` */ | ||
export function replacePathParams(path, params) { | ||
return path.replace(/:([^/]+)/g, (o, v) => { var _a; return (_a = params[v]) !== null && _a !== void 0 ? _a : o; }); | ||
} | ||
/** Makes sure path has leading "/" and no trailing "/" */ | ||
@@ -35,6 +50,1 @@ export function formatPath(path) { | ||
} | ||
/** Attribute is true only if it's not null and value not "false" */ | ||
export function isAttributeTrue(el, attrName) { | ||
const attr = el.getAttribute(attrName); | ||
return attr != null && attr !== 'false'; | ||
} |
{ | ||
"name": "@bjornlu/svelte-router", | ||
"description": "Simple router for Svelte", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"main": "dist/svelte-router.umd.js", | ||
@@ -56,4 +56,6 @@ "module": "dist/index.js", | ||
"@rollup/plugin-typescript": "^5.0.2", | ||
"@types/jest": "^26.0.10", | ||
"cypress": "^4.10.0", | ||
"jest": "^26.4.2", | ||
"jest-transform-stub": "^2.0.0", | ||
"prettier": "^2.0.5", | ||
@@ -72,2 +74,2 @@ "prettier-plugin-svelte": "^1.1.0", | ||
} | ||
} | ||
} |
@@ -6,7 +6,6 @@ # Svelte Router | ||
[![npm downloads](https://img.shields.io/npm/dm/@bjornlu/svelte-router)](https://www.npmjs.com/package/@bjornlu/svelte-router) | ||
[![bundle size](https://img.shields.io/bundlephobia/minzip/@bjornlu/svelte-router)](https://bundlephobia.com/result?p=@bjornlu/svelte-router) | ||
[![ci](https://github.com/bluwy/svelte-router/workflows/CI/badge.svg?event=push)](https://github.com/bluwy/svelte-router/actions) | ||
[![e2e](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/vjxpm8/master&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/vjxpm8/runs) | ||
A straight-forward and easy-to-use SPA router. | ||
An easy-to-use SPA router for Svelte. | ||
@@ -18,5 +17,5 @@ > npm install @bjornlu/svelte-router | ||
- Super simple API | ||
- Support hash and [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) navigation | ||
- Support `hash` and `path` based navigation | ||
- Auto `base` tag navigation | ||
- Easy redirection and navigation guards (with async support) | ||
- Easy [redirection and navigation guards](./Recipes.md#redirects-and-navigation-guard) (with async support) | ||
- Define all routes in one object | ||
@@ -34,5 +33,6 @@ - Nested routes | ||
- [Usage](#usage) | ||
- [Setup](#setup) | ||
- [Navigation](#navigation) | ||
- [Route info](#route-info) | ||
- [API](#api) | ||
- [`<Link />`](#link) | ||
- [`navigate`](#navigate) | ||
- [`route`](#route) | ||
- [Recipes](./Recipes.md) | ||
@@ -42,7 +42,5 @@ | ||
### Setup | ||
```js | ||
// router.js | ||
Before mounting your app, initialize the router: | ||
```js | ||
import { initRouter } from '@bjornlu/svelte-router' | ||
@@ -55,6 +53,7 @@ import Home from './Home.svelte' | ||
// Initializes the router. Subsequent calls to this function are ignored. | ||
// Initialize the router | ||
initRouter({ | ||
// The routing mode: "hash" or "history". Default: "hash". | ||
// The routing mode: "hash" or "path" | ||
mode: 'history', | ||
// Define routes from the most specific to the least specific | ||
routes: [ | ||
@@ -66,3 +65,5 @@ { | ||
{ | ||
// Use ":variable" to use named parameters | ||
path: '/profile/:id', | ||
// Component with childrens must have a <slot /> | ||
component: Profile, | ||
@@ -95,6 +96,2 @@ children: [ | ||
> Make sure routes are defined in the order of the most specific to the least. | ||
Then, in your app add `<RouterView />`: | ||
```svelte | ||
@@ -112,12 +109,19 @@ <!-- App.svelte --> | ||
Done! | ||
```js | ||
// main.js | ||
> Wait. How does `<RouterView />` render nested components? The answer is that it renders them in the component's default `<slot />`. So make sure a slot tag is defined for all routes' component with children. | ||
import App from './App.svelte' | ||
import './router' | ||
### Navigation | ||
const app = new App({ | ||
target: document.getElementById('app') | ||
}) | ||
Svelte Router provides 3 ways for navigation. Each with its own use cases: | ||
export default app | ||
``` | ||
#### `<Link />` component | ||
## API | ||
### `<Link />` | ||
| Prop | Type | Default | Description | | ||
@@ -130,3 +134,3 @@ | --- | --- | --- | --- | | ||
- Adds `aria-current="page"` when link exactly matches | ||
- Display correct `href` by resolving base path, param shorthand paths and hash prepend, e.g. `/foo/:id` => `/foo/123` | ||
- Display correct `href` by resolving base path, param shorthand paths and hash prepends, e.g. `/foo/:id` => `/foo/123` | ||
- Adds active class names based on `to` and current route path: | ||
@@ -136,31 +140,19 @@ - `link-active` when link partially matches, e.g. `/foo` matches `/foo/bar` | ||
#### `use:link` action | ||
### `navigate` | ||
- Used on anchor tag to use its `href` as target route | ||
- Can also be used on any element, which its descendant anchor tags will all be applied | ||
- `href` value must start with `/`, `?` or `#` so that it routes | ||
- Add `replace` attribute on anchor tag to replace the route instead of pushing | ||
- Add `noroute` attribute on anchor tag to ignore routing | ||
While similar to the `<Link />` component, it does not do a few things: | ||
- No `aria-current="page"` accessibility | ||
- No `href` resolve, e.g. `/foo/:id` will be shown as is | ||
- No active class names | ||
It's recommended to use the `<Link />` component wherever possible so that the `href` resolves to a valid one. So for example when the user control-clicks the link (open in new tab), it opens a valid route. | ||
#### `navigate` function | ||
`navigate` has two function signatures: | ||
##### `navigate(to: number)` | ||
1. `navigate(to: number)` | ||
Navigate using an offset in the current history. Works the same way as [`history.go`](https://developer.mozilla.org/en-US/docs/Web/API/History/go). | ||
##### `navigate(to: string | LocationInput, replace?: boolean)` | ||
2. `navigate(to: string | LocationInput, replace?: boolean)` | ||
Navigate to a route using a string or an object. `string` and `LocationInput` are semantically equal and are just different ways to express the same route. For example, `/foo?key=value#top` is the same as: | ||
Navigate to a route using a string or an object. `string` and `LocationInput` are semantically equal and are just different ways to express the same route. For example: | ||
```js | ||
'/foo?key=value#top' | ||
// same as | ||
{ | ||
@@ -190,12 +182,12 @@ path: '/foo', | ||
### Route info | ||
### `route` | ||
Svelte Router exports a readable store `route` that contains the current route's information. | ||
Svelte Router exports a readable store `route` that contains the current route information. | ||
| Property | Type | Example | Description | | ||
| --- | --- | --- | --- | | ||
| path | string | `/foo` | | | ||
| path | string | `'/foo'` | | | ||
| params | Record<string, string> | `{ id: '123' }` | The parsed path parameters, e.g. /foo/:id | | ||
| search | URLSearchParams | | The path search parsed with URLSearchParams | | ||
| hash | string | `#hey` | The path hash with leading `#`. Empty string if no hash. | | ||
| hash | string | `'#hey'` | The path hash with leading `#`. Empty string if no hash. | | ||
| matched | RouteRecord[] | | The array of route records for all nested path segments of the current route. The matched records reference the records defined in the `routes` configuration. | | ||
@@ -202,0 +194,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
74733
19
25
1867
195