[HYPERAPP] hyperapp-tutorial-step-4 (ok)
https://codesandbox.io/s/hyperapp-tutorial-step-4-8u9q8
C:\xampp\htdocs\hyperlist\index.html
<!DOCTYPE html>
<html>
<head>
<title>Hyperapp Tutorial - Step 1</title>
<link rel="stylesheet" href="style.css" />
<script type="module" src="./index.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>C:\xampp\htdocs\hyperlist\index.js
import { h, app } from "hyperapp"
// -- EFFECTS & SUBSCRIPTIONS --
const fetchJSONData = (dispatch, options) => {
dispatch(options.onstart)
fetch(options.url)
.then(response => response.json())
.then(data => dispatch(options.onresponse, data))
.catch(() => dispatch(options.onresponse, {}))
.finally(() => dispatch(options.onfinish))
}
const intervalSubscription = (dispatch, options) => {
const interval = setInterval(() => dispatch(options.action), options.time)
return () => clearInterval(interval)
}
// -- EFFECT CREATORS --
const storyLoader = searchWord => [
fetchJSONData,
{
url: `https://hyperapp.dev/tutorial-assets/stories/${searchWord.toLowerCase()}.json`,
onresponse: GotStories,
onstart: [SetFetching, true],
onfinish: [SetFetching, false],
},
]
// -- ACTIONS --
const StartEditingFilter = state => ({ ...state, editingFilter: true })
const StopEditingFilter = state => [{
...state,
editingFilter: false,
},
storyLoader(state.filter),
]
const SetFilter = (state, word) => ({ ...state, filter: word })
const SelectStory = (state, id) => ({
...state, // keep all state the same, except for the following:
reading: id,
stories: {
...state.stories, //keep stories the same, except for:
[id]: {
...state.stories[id], //keep this story the same, except for:
seen: true,
},
},
})
const GotStories = (state, stories) => ({
...state,
// replace old stories with new,
// but keep the 'seen' value if it exists
stories: Object.keys(stories)
.map(id => [
id,
{
...stories[id],
seen: state.stories[id] && state.stories[id].seen,
},
])
.reduce((o, [id, story]) => ((o[id] = story), o), {}),
// in case the current story is in the new list as well,
// keep it selected, Otherwise select nothing
reading: stories[state.reading] ? state.reading : null,
})
const SetFetching = (state, fetching) => ({ ...state, fetching })
const ToggleAutoUpdate = state => ({
...state,
autoUpdate: !state.autoUpdate,
})
// -- VIEWS ---
const emphasize = (word, string) =>
string.split(" ").map(x => {
if (x.toLowerCase() === word.toLowerCase()) {
return h("em", {}, x + " ")
} else {
return x + " "
}
})
const storyThumbnail = props =>
h(
"li", {
onclick: [SelectStory, props.id],
class: {
unread: props.unread,
reading: props.reading,
},
},
[
h("p", { class: "title" }, emphasize(props.filter, props.title)),
h("p", { class: "author" }, props.author),
],
)
const storyList = props =>
h("div", { class: "stories" }, [
// show spinner overlay if fetching
props.fetching &&
h("div", { class: "loadscreen" }, [h("div", { class: "spinner" })]),
h(
"ul", {},
Object.keys(props.stories).map(id =>
storyThumbnail({
id,
title: props.stories[id].title,
author: props.stories[id].author,
unread: !props.stories[id].seen,
reading: props.reading === id,
filter: props.filter,
}),
),
),
])
const filterView = props =>
h("div", { class: "filter" }, [
"Filter:",
props.editingFilter ?
h("input", {
type: "text",
value: props.filter,
oninput: [SetFilter, event => event.target.value], // <----
}) :
h("span", { class: "filter-word" }, props.filter),
props.editingFilter ?
h("button", { onclick: StopEditingFilter }, "\u2713") :
h("button", { onclick: StartEditingFilter }, "\u270E"),
])
const storyDetail = props =>
h("div", { class: "story" }, [
props && h("h1", {}, props.title),
props &&
h(
"p", {},
`Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, qui
nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat.`,
),
props && h("p", { class: "signature" }, props.author),
])
const autoUpdateView = props =>
h("div", { class: "autoupdate" }, [
"Auto update: ",
h("input", {
type: "checkbox",
checked: props.autoUpdate,
oninput: ToggleAutoUpdate,
}),
])
const container = content => h("div", { class: "container" }, content)
// -- RUN --
app({
node: document.getElementById("app"),
init: [{
editingFilter: false,
autoUpdate: false,
filter: "ocean",
reading: null,
stories: {},
},
storyLoader("ocean"),
],
view: state =>
container([
filterView(state),
storyList(state),
storyDetail(state.reading && state.stories[state.reading]),
autoUpdateView(state),
]),
subscriptions: state => [
state.autoUpdate &&
!state.editingFilter && [
intervalSubscription,
{
time: 5000, //milliseconds,
action: StopEditingFilter,
},
],
],
})C:\xampp\htdocs\hyperlist\package.json
C:\xampp\htdocs\hyperlist\style.css
Last updated
Was this helpful?