Skip to content

Commit ddd2c57

Browse files
committed
Add a NavLink with click capturing
1 parent 74af61d commit ddd2c57

File tree

5 files changed

+70
-30
lines changed

5 files changed

+70
-30
lines changed

src/app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { run } from '@cycle/run';
22
import { App } from './components/App';
33
import { makeDOMDriver } from '@cycle/dom';
4-
import { makeHashHistoryDriver } from '@cycle/history';
4+
import { captureClicks, makeHashHistoryDriver } from '@cycle/history';
55
import { cssRaw } from 'typestyle';
66
import { normalize, setupPage } from 'csstips';
77

@@ -30,5 +30,5 @@ cssRaw(`
3030

3131
run(App, {
3232
dom: makeDOMDriver('#app'),
33-
history: makeHashHistoryDriver()
33+
history: captureClicks(makeHashHistoryDriver())
3434
});

src/components/Header/NavMenu.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import { Stream } from 'xstream';
1+
import { Stream, MemoryStream } from 'xstream';
22
import { DOMSource, VNode, nav, a } from '@cycle/dom';
33
import isolate from '@cycle/isolate';
44
import { style } from 'typestyle';
55
import { rem } from 'csx';
6+
import { NavLink } from '../NavLink';
7+
import { Location } from '@cycle/history';
68

79
interface Sources {
810
dom: DOMSource;
11+
history: MemoryStream<Location>;
912
}
1013

1114
interface Sinks {
1215
dom: Stream<VNode>;
13-
history: Stream<string>;
1416
}
1517

1618
const navLink = {
@@ -43,24 +45,27 @@ const className = style({
4345
});
4446

4547
const xs = Stream;
48+
const menuItems = [
49+
{
50+
href: '/',
51+
title: 'Home'
52+
},
53+
{
54+
href: '/commits',
55+
title: 'Commits'
56+
},
57+
{
58+
href: '/about',
59+
title: 'About'
60+
}
61+
];
4662

47-
const NavMenuComponent = ({ dom }: Sources): Sinks => {
48-
const navigateTo$ =
49-
dom.select('a')
50-
.events('click', { preventDefault: true })
51-
.map(ev => (ev.target as HTMLAnchorElement).href)
52-
.debug();
53-
const vdom$ =
54-
xs.of(
55-
nav(`.${className}`, [
56-
a({ attrs: { href: '#/', title: 'Home' } }, ['Home']),
57-
a({ attrs: { href: '#/commits', title: 'Commits' } }, ['Commits']),
58-
a({ attrs: { href: '#/about', title: 'About' } }, ['About']),
59-
])
60-
);
63+
const NavMenuComponent = ({ dom, history }: Sources): Sinks => {
64+
const navLinks = menuItems.map(({ href, title }) => NavLink({ dom, history, href$: xs.of(href), title$: xs.of(title) }));
65+
const navLinksDom$ = xs.combine(...navLinks.map(navLink => navLink.dom))
66+
const vdom$ = navLinksDom$.map(navLinks => nav(`.${className}`, navLinks));
6167
return {
62-
dom: vdom$,
63-
history: navigateTo$
68+
dom: vdom$
6469
};
6570
};
6671

src/components/Header/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import { Stream } from 'xstream';
1+
import { Stream, MemoryStream } from 'xstream';
22
import { DOMSource, VNode, div, h1, em, br } from '@cycle/dom';
33
import { style } from 'typestyle';
44
import { rem } from 'csx';
55
import { NavMenu } from './NavMenu';
6+
import { Location } from '@cycle/history';
67

78
interface Sources {
89
dom: DOMSource;
10+
history: MemoryStream<Location>;
911
}
1012

1113
interface Sinks {
1214
dom: Stream<VNode>;
13-
history: Stream<string>;
1415
}
1516

1617
const className = style({
@@ -33,8 +34,8 @@ const className = style({
3334

3435
const xs = Stream;
3536

36-
export const Header = ({ dom }: Sources): Sinks => {
37-
const navMenu = NavMenu({ dom });
37+
export const Header = ({ dom, history }: Sources): Sinks => {
38+
const navMenu = NavMenu({ dom, history });
3839
const vdom$ = navMenu.dom.map(navMenu =>
3940
div(`.${className}`, [
4041
h1([
@@ -46,7 +47,6 @@ export const Header = ({ dom }: Sources): Sinks => {
4647
])
4748
);
4849
return {
49-
dom: vdom$,
50-
history: navMenu.history
50+
dom: vdom$
5151
};
5252
};

src/components/NavLink.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Stream, MemoryStream } from 'xstream';
2+
import { DOMSource, VNode, a } from '@cycle/dom';
3+
import { Location } from '@cycle/history';
4+
import isolate from '@cycle/isolate';
5+
6+
interface Sources {
7+
dom: DOMSource;
8+
history: MemoryStream<Location>;
9+
href$: Stream<string>;
10+
title$: Stream<string>;
11+
}
12+
13+
interface Sinks {
14+
dom: Stream<VNode>;
15+
history: Stream<string>;
16+
}
17+
18+
const xs = Stream;
19+
20+
const NavLinkComponent = ({ dom, history, href$, title$ }: Sources): Sinks => {
21+
const currentHref$ = history.map(location => location.pathname);
22+
const active$ =
23+
currentHref$
24+
.map(href => href$.debug().map(h => href === h))
25+
.flatten();
26+
const vdom$ =
27+
xs.combine(href$, active$, title$)
28+
.map(([ href, active, title ]) => a(`${!!active ? '.active' : ''}`, { attrs: { href, title } }, [title]));
29+
return {
30+
dom: vdom$,
31+
history: xs.empty()
32+
};
33+
};
34+
35+
export const NavLink = (sources: Sources): Sinks => isolate(NavLinkComponent)(sources);

src/layouts/HeaderLayout.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ const className = style({
1919

2020
const xs = Stream;
2121

22-
export const HeaderLayout: Layout = ({ dom, component: { dom: componentDom, history } }) => {
23-
const headerDom$ = Header({ dom }).dom;
22+
export const HeaderLayout: Layout = ({ dom, history, component: { dom: componentDom, history: componentHistory } }) => {
23+
const headerComponent = Header({ dom, history });
2424
const vdom$ =
25-
xs.combine(headerDom$, componentDom)
25+
xs.combine(headerComponent.dom, componentDom)
2626
.map(([headerDom, component]) =>
2727
div(`.header.layout.${className}`, [
2828
header(headerDom),
@@ -32,6 +32,6 @@ export const HeaderLayout: Layout = ({ dom, component: { dom: componentDom, hist
3232
);
3333
return {
3434
dom: vdom$,
35-
history
35+
history: componentHistory
3636
};
3737
};

0 commit comments

Comments
 (0)