Skip to content

Commit

Permalink
Finished blog
Browse files Browse the repository at this point in the history
  • Loading branch information
chorobinaccess committed Dec 22, 2018
1 parent 151b5f2 commit be9f662
Show file tree
Hide file tree
Showing 29 changed files with 321 additions and 0 deletions.
24 changes: 24 additions & 0 deletions cycle/components/main/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { of, combineLatest } from "rxjs";
import { DOMSource } from "@cycle/dom/lib/es6/rxjs";
import { TaskListComponent } from "../task-list/index";
import { view, renderModalContent } from "./view";
import { ModalComponent } from "../modal/index";
import { SearchComponent } from "../search";
import { model } from "./model";
import { Task } from "../../../models/task";

const tasks: Task[] = [
{ title: "Washing up", description: "Cleaning the dishes", due: new Date(), assignee: "Bob Norris" },
{ title: "Hoovering", description: "The entire house", due: new Date(), assignee: "Cat Norris" },
{ title: "Car Cleaning", description: "Hoovering and dusting the car", due: new Date(), assignee: "Alice Norris" },
];

export const MainComponent = (sources: { DOM: DOMSource }) => {
const search = SearchComponent(sources);
const state$ = model(search.value, of(tasks));
const taskList = TaskListComponent({ props: state$ });
const modalContent = combineLatest(taskList.DOM, search.DOM, renderModalContent);
const modal = ModalComponent({ props: modalContent });
const vnode$ = view(modal.DOM);
return { DOM: vnode$ };
};
8 changes: 8 additions & 0 deletions cycle/components/main/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Observable, combineLatest } from "rxjs";
import { Task } from "../../../models/task";
import { startWith } from "rxjs/operators";

const searchPredicate = (search: string) => (task: Task) => !search || task.title.startsWith(search) || task.description.startsWith(search);

export const model = (search$: Observable<string>, tasks$: Observable<Task[]>) =>
combineLatest(search$.pipe(startWith("")), tasks$, (search, tasks) => tasks.filter(searchPredicate(search)));
19 changes: 19 additions & 0 deletions cycle/components/main/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { main, mainContainer } from "../../../styles/main/main.css";
import { VNode } from "snabbdom/vnode";
import { map } from "rxjs/operators";
const { html } = require("snabbdom-jsx");

const renderMain = (children: VNode) => (
<div class={{ [mainContainer]: true }}>
<div class={{ [main]: true }}>{children}</div>
</div>
);

export const renderModalContent = (taskList: VNode, search: VNode) => (
<div>
{search}
{taskList}
</div>
);

export const view = map(renderMain);
7 changes: 7 additions & 0 deletions cycle/components/modal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Observable } from "rxjs";
import { VNode } from "snabbdom/vnode";
import { view } from "./view";

export const ModalComponent = (sources: { props: Observable<VNode> }) => ({
DOM: view(sources.props),
});
7 changes: 7 additions & 0 deletions cycle/components/modal/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { html } = require("snabbdom-jsx");
import { modal } from "../../../styles/modal/modal-styles.css";
import { map } from "rxjs/operators";
import { VNode } from "snabbdom/vnode";

const renderModal = (children: VNode) => <div class={{ [modal]: true }}>{children}</div>;
export const view = map(renderModal);
8 changes: 8 additions & 0 deletions cycle/components/search/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { view } from "./view";
import { DOMSource } from "@cycle/dom/lib/es6/rxjs";
import { intent } from "./intent";

export const SearchComponent = ({ DOM }: { DOM: DOMSource }) => ({
DOM: view,
value: intent(DOM),
});
13 changes: 13 additions & 0 deletions cycle/components/search/intent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { DOMSource } from "@cycle/dom/lib/es6/rxjs";
import { search } from "../../../styles/search/search.css";
import { map, distinctUntilChanged, debounceTime } from "rxjs/operators";

export const intent = (domSource: DOMSource) =>
domSource
.select(`.${search}`)
.events("input")
.pipe(
map((e: any) => e.target.value),
distinctUntilChanged(),
debounceTime(500),
);
12 changes: 12 additions & 0 deletions cycle/components/search/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { html } = require("snabbdom-jsx");
import { search, searchInput, searchClose } from "../../../styles/search/search.css";
import { of } from "rxjs";

const renderSearch = (
<div class={{ [search]: true }}>
<input class={{ [searchInput]: true }} type="text" placeholder="Search for tasks" />
<span class={{ [searchClose]: true }} />
</div>
);

export const view = of(renderSearch);
13 changes: 13 additions & 0 deletions cycle/components/task-list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Observable, of, from, merge } from "rxjs";
import { Task } from "../../../models/task";
import { mergeMap, map, toArray } from "rxjs/operators";
import { TaskComponent } from "../task";
import { view } from "./view";

export const TaskListComponent = (sources: { props: Observable<Task[]> }) => ({
DOM: sources.props.pipe(
mergeMap((tasks) => merge(...tasks.map((task) => TaskComponent({ props: of(task) }).DOM)).pipe(toArray())),
map((nodes) => ({ tasks: nodes })),
view,
),
});
7 changes: 7 additions & 0 deletions cycle/components/task-list/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { html } = require("snabbdom-jsx");
import { taskList as taskListStyles } from "../../../styles/task-list/task-list.css";
import { VNode } from "snabbdom/vnode";
import { map } from "rxjs/operators";

const renderTaskList = (tasks: VNode[]) => <div class={{ [taskListStyles]: true }}>{tasks}</div>;
export const view = map(({ tasks }: { tasks: VNode[] }) => renderTaskList(tasks));
7 changes: 7 additions & 0 deletions cycle/components/task/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Observable } from "rxjs";
import { Task } from "../../../models/task";
import { view } from "./view";

export const TaskComponent = (sources: { props: Observable<Task> }) => ({
DOM: view(sources.props),
});
22 changes: 22 additions & 0 deletions cycle/components/task/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { html } = require("snabbdom-jsx");
import { Task } from "../../../models/task";
import {
task as taskStyle,
taskRight as taskStyleRight,
taskTitle as taskStyleTitle,
taskLeft as taskStyleLeft,
taskSelected,
} from "../../../styles/task/task-styles.css";
import { map } from "rxjs/operators";

const renderTask = (task: Task) => (
<div class={{ [taskStyle]: true }}>
<div class={{ [taskSelected]: true }} />
<h3 class={{ [taskStyleTitle]: true, [taskStyleLeft]: true }}>{task.title}</h3>
<span class={{ [taskStyleRight]: true }}>{task.assignee}</span>
<span class={{ [taskStyleLeft]: true }}>{task.description}</span>
<span class={{ [taskStyleRight]: true }}>{task.due.toLocaleDateString()}</span>
</div>
);

export const view = map(renderTask);
6 changes: 6 additions & 0 deletions cycle/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { run } from "@cycle/rxjs-run";
import { makeDOMDriver } from "@cycle/dom/lib/es6/rxjs";
import { MainComponent } from "./components/main/index";
import "snabbdom-jsx";

run(MainComponent, { DOM: makeDOMDriver("#app") });
10 changes: 10 additions & 0 deletions cycle/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "esnext",
"target": "es5",
"jsx": "preserve",
"lib": ["es2015", "dom"],
"moduleResolution": "node",
"reactNamespace": "html"
}
}
3 changes: 3 additions & 0 deletions react/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-react", "@babel/preset-typescript", ["@babel/preset-env", { "modules": false }]]
}
3 changes: 3 additions & 0 deletions react/components/main/main-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface MainState {
readonly search: string;
}
4 changes: 4 additions & 0 deletions react/components/modal/modal-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as React from "react";
import { modal } from "../../../styles/modal/modal-styles.css";

export const ModalComponent: React.SFC = ({ children }) => <div className={modal}>{children}</div>;
10 changes: 10 additions & 0 deletions react/components/search/search-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from "react";
import { search, searchInput, searchClose } from "../../../styles/search/search.css";
import { SearchProps } from "./search-props";

export const SearchComponent: React.SFC<SearchProps> = ({ onSearch }) => (
<div className={search}>
<input className={searchInput} type="text" placeholder="Search for tasks" onChange={(e) => onSearch(e.target.value)} />
<span className={searchClose} />
</div>
);
3 changes: 3 additions & 0 deletions react/components/search/search-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface SearchProps {
readonly onSearch: (text: string) => void;
}
13 changes: 13 additions & 0 deletions react/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"outDir": "./dist",
"sourceMap": true,
"noImplicitAny": true,
"moduleResolution": "node",
"module": "commonjs",
"target": "es5",
"jsx": "react",
"lib": ["es2015", "dom"]
},
"include": ["./"]
}
6 changes: 6 additions & 0 deletions styles/modal/modal-styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.modal {
border: 1px solid;
padding: 20px;
box-shadow: 1px 2px 8px;
border-color: lightgray;
}
1 change: 1 addition & 0 deletions styles/modal/modal-styles.css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const modal: string;
43 changes: 43 additions & 0 deletions styles/search/search.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.search {
display: flex;
flex-flow: row nowrap;
border: solid 1px lightgray;
padding: 5px;
border-radius: 3px;
box-shadow: 1px 1px lightgray;
}

.search__input {
flex: 1 0 auto;
border: none;
}

.search__input:focus {
outline: none;
}

.search__close {
opacity: 0.3;
width: 20px;
flex: 0 0 auto;
}

.search__close:hover {
opacity: 1;
}

.search__close:before,
.search__close:after {
content: " ";
height: 100%;
width: 2px;
background-color: #333;
width: 32px;
}

.search__close:before {
transform: rotate(45deg);
}
.search__close:after {
transform: rotate(-45deg);
}
3 changes: 3 additions & 0 deletions styles/search/search.css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export declare const search: string;
export declare const searchInput: string;
export declare const searchClose: string;
47 changes: 47 additions & 0 deletions webpack.cycle.common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import common from "./webpack.common";
import merge from "webpack-merge";

const config = merge(common, {
entry: "./cycle/index.tsx",
output: {
filename: "bundle.js",
path: __dirname + "/dist",
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-typescript", ["@babel/preset-env", { modules: false }]],
plugins: [["@babel/plugin-syntax-jsx"], ["@babel/plugin-transform-react-jsx", { pragma: "html" }]],
},
},
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
hmr: true,
},
},
{
loader: "css-loader",
options: {
modules: true,
camelCase: true,
},
},
],
},
],
},
plugins: [
/*new ForkTsCheckerPlugin({
tsconfig: "./cycle/tsconfig.json",
}),*/
],
});

export default config;
4 changes: 4 additions & 0 deletions webpack.cycle.dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import cycleCommon from "./webpack.cycle.common";
import devCommon from "./webpack.dev.common";

export default devCommon(cycleCommon);
4 changes: 4 additions & 0 deletions webpack.cycle.prod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import cycleCommon from "./webpack.cycle.common";
import prodCommon from "./webpack.prod.common";

export default prodCommon(cycleCommon);
10 changes: 10 additions & 0 deletions webpack.prod.common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as webpack from "webpack";
import merge from "webpack-merge";
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";

export default (config: webpack.Configuration) => {
return merge(config, {
mode: "production",
plugins: [new BundleAnalyzerPlugin()],
});
};
4 changes: 4 additions & 0 deletions webpack.react.prod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import cycleCommon from "./webpack.react.common";
import prodCommon from "./webpack.prod.common";

export default prodCommon(cycleCommon);

0 comments on commit be9f662

Please sign in to comment.