Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 45 additions & 41 deletions src/routing/ActiveLink.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import WidgetBase from '../core/WidgetBase';
import { WNode, SupportedClassName } from '../core/interfaces';
import diffProperty from '../core/decorators/diffProperty';
import { LinkProperties } from './interfaces';
import Link from './Link';
import { create, diffProperty, invalidator, w } from '../core/vdom';
import { Handle } from '../core/Destroyable';
import injector from '../core/middleware/injector';
import cache from '../core/middleware/cache';
import { SupportedClassName } from '../core/interfaces';
import Link, { LinkProperties } from './Link';
import Router from './Router';
import { Handle } from '../shim/interfaces';
import { w } from '../core/vdom';

export interface ActiveLinkProperties extends LinkProperties {
activeClasses: SupportedClassName[];
Expand All @@ -15,50 +14,55 @@ function paramsEqual(linkParams: any = {}, contextParams: any = {}) {
return Object.keys(linkParams).every((key) => linkParams[key] === contextParams[key]);
}

export class ActiveLink extends WidgetBase<ActiveLinkProperties> {
private _outletHandle: Handle | undefined;
const factory = create({ injector, diffProperty, cache, invalidator }).properties<ActiveLinkProperties>();

private _renderLink(isActive = false) {
let { activeClasses, classes = [], ...props } = this.properties;
classes = Array.isArray(classes) ? classes : [classes];
if (isActive) {
classes = [...classes, ...activeClasses];
}
props = { ...props, classes };
return w(Link, props, this.children);
}
export const ActiveLink = factory(function ActiveLink({
middleware: { diffProperty, injector, cache, invalidator },
properties,
children
}) {
const { to, routerKey = 'router', params } = properties;
let { activeClasses, classes = [], ...props } = properties;

@diffProperty('to')
protected _onOutletPropertyChange(previous: ActiveLinkProperties, current: ActiveLinkProperties) {
const { to, routerKey = 'router' } = current;
const item = this.registry.getInjector<Router>(routerKey);
if (this._outletHandle) {
this._outletHandle.destroy();
this._outletHandle = undefined;
diffProperty('to', (current: ActiveLinkProperties, next: ActiveLinkProperties) => {
if (current.to !== next.to) {
const router = injector.get<Router>(routerKey);
const currentHandle = cache.get<Handle>('handle');
if (currentHandle) {
currentHandle.destroy();
}
if (router) {
const handle = router.on('outlet', ({ outlet }) => {
if (outlet.id === to) {
invalidator();
}
});
cache.set('handle', handle);
}
invalidator();
}
if (item) {
const router = item.injector();
this._outletHandle = router.on('outlet', ({ outlet }) => {
});

const router = injector.get<Router>(routerKey);
if (router) {
if (!cache.get('handle')) {
const handle = router.on('outlet', ({ outlet }) => {
if (outlet.id === to) {
this.invalidate();
invalidator();
}
});
cache.set('handle', handle);
}
}

protected render(): WNode {
const { to, routerKey = 'router', params } = this.properties;
const item = this.registry.getInjector<Router>(routerKey);
if (!item) {
return this._renderLink();
}
const router = item.injector();
const context = router.getOutlet(to);

const isActive = context && paramsEqual(params, context.params);

return this._renderLink(isActive);
classes = Array.isArray(classes) ? classes : [classes];
if (isActive) {
classes = [...classes, ...activeClasses];
}
props = { ...props, classes };
}
}
return w(Link, props, children);
});

export default ActiveLink;
63 changes: 35 additions & 28 deletions src/routing/Link.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import { WidgetBase } from '../core/WidgetBase';
import { VNode } from '../core/interfaces';
import { LinkProperties } from './interfaces';
import { Router } from './Router';
import { v } from '../core/vdom';
import { create, v } from '../core/vdom';
import injector from '../core/middleware/injector';
import { VNodeProperties } from '../core/interfaces';
import { Params } from './interfaces';
import Router from './Router';

export class Link extends WidgetBase<LinkProperties> {
private _getProperties() {
let { routerKey = 'router', to, isOutlet = true, target, params = {}, onClick, ...props } = this.properties;
const item = this.registry.getInjector<Router>(routerKey);
let href: string | undefined = to;
export interface LinkProperties extends VNodeProperties {
key?: string;
routerKey?: string;
isOutlet?: boolean;
params?: Params;
onClick?: (event: MouseEvent) => void;
to: string;
}

if (item) {
const router = item.injector();
if (isOutlet) {
href = router.link(href, params);
}
const onclick = (event: MouseEvent) => {
onClick && onClick(event);
const factory = create({ injector }).properties<LinkProperties>();

export const Link = factory(function Link({ middleware: { injector }, properties, children }) {
let { routerKey = 'router', to, isOutlet = true, target, params = {}, onClick, ...props } = properties;
const router = injector.get<Router>(routerKey);
let href: string | undefined = to;

if (!event.defaultPrevented && event.button === 0 && !event.metaKey && !event.ctrlKey && !target) {
event.preventDefault();
href !== undefined && router.setPath(href);
}
};
return { ...props, onclick, href };
let linkProps: VNodeProperties;
if (router) {
if (isOutlet) {
href = router.link(href, params);
}
return { ...props, href };
}
const onclick = (event: MouseEvent) => {
onClick && onClick(event);

protected render(): VNode {
return v('a', this._getProperties(), this.children);
if (!event.defaultPrevented && event.button === 0 && !event.metaKey && !event.ctrlKey && !target) {
event.preventDefault();
href !== undefined && router.setPath(href);
}
};
linkProps = { ...props, onclick, href };
} else {
linkProps = { ...props, href };
}
}
return v('a', linkProps, children);
});

export default Link;
82 changes: 39 additions & 43 deletions src/routing/Outlet.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,56 @@
import { create, diffProperty, invalidator } from '../core/vdom';
import injector from '../core/middleware/injector';
import cache from '../core/middleware/cache';
import { DNode } from '../core/interfaces';
import { WidgetBase } from '../core/WidgetBase';
import { alwaysRender } from '../core/decorators/alwaysRender';
import { MatchDetails } from './interfaces';
import { Router } from './Router';
import { diffProperty } from '../core/decorators/diffProperty';
import { Handle } from '../core/Destroyable';
import Router from './Router';

export interface OutletProperties {
renderer: (matchDetails: MatchDetails) => DNode | DNode[];
id: string;
routerKey?: string;
}

@alwaysRender()
export class Outlet extends WidgetBase<OutletProperties> {
private _handle: Handle | undefined;
const factory = create({ cache, injector, diffProperty, invalidator }).properties<OutletProperties>();

@diffProperty('routerKey')
protected onRouterKeyChange(current: OutletProperties, next: OutletProperties) {
const { routerKey = 'router' } = next;
const item = this.registry.getInjector<Router>(routerKey);
if (this._handle) {
this._handle.destroy();
this._handle = undefined;
}
if (item) {
this._handle = item.invalidator.on('invalidate', () => {
this.invalidate();
});
this.own(this._handle);
export const Outlet = factory(function Outlet({
middleware: { cache, injector, diffProperty, invalidator },
properties
}) {
const { renderer, id, routerKey = 'router' } = properties;
const currentHandle = cache.get<Function>('handle');
if (!currentHandle) {
const handle = injector.subscribe(routerKey);
if (handle) {
cache.set('handle', handle);
}
}

protected onAttach() {
if (!this._handle) {
this.onRouterKeyChange(this.properties, this.properties);
diffProperty('routerKey', (current: OutletProperties, next: OutletProperties) => {
const { routerKey: currentRouterKey = 'router' } = current;
const { routerKey = 'router' } = next;
if (routerKey !== currentRouterKey) {
const currentHandle = cache.get<Function>('handle');
if (currentHandle) {
currentHandle();
}
const handle = injector.subscribe(routerKey);
if (handle) {
cache.set('handle', handle);
}
}
}

protected render(): DNode | DNode[] {
const { renderer, id, routerKey = 'router' } = this.properties;
const item = this.registry.getInjector<Router>(routerKey);
invalidator();
});
const router = injector.get<Router>(routerKey);

if (item) {
const router = item.injector();
const outletContext = router.getOutlet(id);
if (outletContext) {
const { queryParams, params, type, isError, isExact } = outletContext;
const result = renderer({ queryParams, params, type, isError, isExact, router });
if (result) {
return result;
}
if (router) {
const outletContext = router.getOutlet(id);
if (outletContext) {
const { queryParams, params, type, isError, isExact } = outletContext;
const result = renderer({ queryParams, params, type, isError, isExact, router });
if (result) {
return result;
}
}
return null;
}
}

export default Outlet;
return null;
});
Loading