From: Eduardo San Martin Morote Date: Thu, 7 Feb 2019 19:20:49 +0000 (+0100) Subject: add more code X-Git-Tag: v4.0.0-alpha.0~481 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=863c480fb9dff7a118f7e5405a61183ac00fccff;p=thirdparty%2Fvuejs%2Frouter.git add more code --- diff --git a/src/history/base.ts b/src/history/base.ts index 7cd5ae61..8633930a 100644 --- a/src/history/base.ts +++ b/src/history/base.ts @@ -1,13 +1,20 @@ -import { START, Location } from '../types/index' +import { START, HistoryLocation } from '../types/index' export interface NavigationCallback { - (location: string): void + (to: HistoryLocation, from: HistoryLocation): void } export default abstract class BaseHistory { // previousState: object - location: Location - abstract push(to: Location): void + location: HistoryLocation + abstract push(to: HistoryLocation): void + abstract replace(to: HistoryLocation): void abstract listen(callback: NavigationCallback): Function + /** + * ensure the current location using the external source + * for example, in HTML5 and hash history, that would be + * location.pathname + */ + abstract ensureLocation(): void constructor() { this.location = START diff --git a/src/history/html5.ts b/src/history/html5.ts index edf0b317..ccd63e68 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -1,26 +1,52 @@ import BaseHistory, { NavigationCallback } from './base' -import { Location } from '../types/index' +import { HistoryLocation } from '../types/index' export default class HTML5History extends BaseHistory { - history: typeof window.history + private history = window.history constructor() { super() - this.history = window.history } - push(to: Location): void { - // TODO resolve url - this.history.pushState( + ensureLocation() { + const to = window.location.pathname + this.replace(to) + } + + replace(to: HistoryLocation) { + if (to === this.location) return + this.history.replaceState( { - from: this.location, + ...(this.history.state || {}), to, }, '', to ) + this.location = to + } + + push(to: HistoryLocation) { + // TODO resolve url + // TODO compare current location to prevent navigation + if (to === this.location) return + const state = { + from: this.location, + to, + } + console.log('push', state) + this.history.pushState(state, '', to) + this.location = to } listen(callback: NavigationCallback): Function { + window.addEventListener('popstate', ({ state }) => { + const from = this.location + // we have the state from the old entry, not the current one being removed + // TODO correctly parse pathname + this.location = state ? state.to : window.location.pathname + callback(this.location, from) + }) + return () => {} } } diff --git a/src/index.ts b/src/index.ts index 8de295db..14b7c8b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,71 @@ -class Router {} +import BaseHistory from './history/base' +import pathToRegexp from 'path-to-regexp' +import { Location, RouteRecord } from './types/index' + +interface RouterOptions { + history: BaseHistory + routes: RouteRecord[] +} + +interface RouteMatcher { + re: RegExp + record: RouteRecord + keys: string[] +} + +export class Router { + protected history: BaseHistory + private routes: RouteMatcher[] + + constructor(options: RouterOptions) { + this.history = options.history + this.history.ensureLocation() + + this.routes = options.routes.map(record => { + const keys: pathToRegexp.Key[] = [] + // TODO: if children use option end: false ? + const re = pathToRegexp(record.path, keys) + return { re, keys: keys.map(k => '' + k.name), record } + }) + } + + /** + * Trigger a navigation, should resolve all guards first + * @param to Where to go + */ + push(to: Location) { + // TODO: resolve URL + const path = this.resolve(to) + // TODO: call hooks, guards + this.history.push(path) + } + + getRouteRecord(location: Location) {} + + /** + * Transforms a Location object into a URL string. If a string is + * passed, it returns the string itself + * @param location Location to resolve to a url + */ + resolve(location: Location): string { + if (typeof location === 'string') return location + if ('path' in location) { + // TODO: convert query, hash, warn params + return location.path + } + + if (!('name' in location)) { + // TODO: use current location + // location = {...location, name: this.} + return '/heeey' + } + const matcher = this.routes.find(r => r.record.name === location.name) + if (!matcher) { + // TODO: error + throw new Error('No match for' + location) + } + matcher.re + return '/TODO' + // this.matcher.match(location) + } +} diff --git a/src/types/index.ts b/src/types/index.ts index 43760487..39a94194 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,10 +1,10 @@ type TODO = any -type ParamsType = Record +export type ParamsType = Record -interface PropsTransformer { - (params: ParamsType): any -} +// interface PropsTransformer { +// (params: ParamsType): any +// } // export interface Location { // record: RouteRecord @@ -16,14 +16,23 @@ interface PropsTransformer { // since in callbacks we don't know where we are coming from // and I don't thin it's possible to filter out the route // by any means -export interface RouteRecord { +export interface RouteRecord { path: string | RegExp component: TODO - name: string - props: PT + name?: string + // props: PT } // TODO location should be an object -export type Location = string +export type Location = + | string + | { + path: string + } + | { + name: string + params?: Record + } +export type HistoryLocation = string -export const START: Location = '' +export const START: HistoryLocation = '/' diff --git a/tsconfig.json b/tsconfig.json index 6d385490..b84769fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist" /* Redirect output structure to the directory. */, // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */