diff --git a/.eslintrc.json b/.eslintrc.json index 3cc4182..4a7f0c4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -28,7 +28,7 @@ } }, { - "files": ["src/**/*.js"], + "files": ["src/**/*.mjs"], "env": { "browser": true, "es6": true diff --git a/src/js/main.mjs b/src/js/main.mjs index 134a980..e5a5ab2 100644 --- a/src/js/main.mjs +++ b/src/js/main.mjs @@ -1,66 +1,83 @@ -import { app, h, text } from 'https://cdn.skypack.dev/hyperapp@2'; +//@ts-check +import { reactive, html } from 'https://esm.sh/@arrow-js/core'; -const initialState = { - status: 'idle', - repos: [], -}; +/** + * @typedef {Object} Repository + * @property {string} name + * @property {string} topics + * @property {string} homepageUrl + * @property {string} description + * @property {string} url + */ -const SetStatus = (state, status) => ({ ...state, status }); +/** + * @typedef {Object} Data + * @property {'idle' | 'loading' | 'loaded' | 'error'} status + * @property {Repository[]} repos + */ -const GotProjects = (state, repos = []) => ({ - ...SetStatus(state, 'loaded'), - repos, +/** + * @type {Data} + */ +const data = reactive({ + status: 'idle', + repos: [], }); -function branch(prop, branches) { - return branches[prop] || branches.default; +function branch(branches) { + return branches[data.status] || branches.default; } -async function FetchProjects(dispatch) { - dispatch(SetStatus, 'loading'); +async function fetchProjects() { + data.status = 'loading'; try { const response = await fetch('/.netlify/functions/fetch-projects'); if (!response.ok) { throw response; } - dispatch(GotProjects, await response.json()); + Object.assign(data, { + status: 'loaded', + repos: await response.json(), + }); } catch (error) { console.error(error); - dispatch(SetStatus, 'error'); + Object.assign(data, { + status: 'error', + repos: [], + }); } } -function Link(href, txt) { - return h('a', { target: '_blank', href }, text(txt)); +/** + * + * @param {Repository} repository + * @returns + */ +function RepoItem({ name, topics, homepageUrl, description, url }) { + return html`
+
+

+ ${name} +

+
+

+ ${description}
+ + GitHub + +

+

${topics.map((txt) => html`${txt}`)}

+
`.key(url); } -function Label(txt) { - return h('span', { class: 'label' }, text(txt)); -} +const template = html`${() => + branch({ + loading: html`

Loading...

`, + loaded: data.repos.map(RepoItem), + error: html`

Error!

`, + })}`; -function RepoItem({ name, topics, homepageUrl, description, url }) { - return h('article', { class: 'card' }, [ - h('header', {}, h('h2', {}, Link(homepageUrl, name))), - h('p', {}, [ - h('div', {}, text(description)), - h('small', {}, Link(url, 'GitHub')), - ]), - h('p', { class: 'cluster' }, topics.map(Label)), - ]); -} +template(document.getElementById('main')); -app({ - init: [initialState, [FetchProjects]], - node: document.getElementById('main'), - view: ({ repos, status }) => - h( - 'main', - {}, - branch(status, { - loading: h('p', {}, text('Loading...')), - loaded: h('div', {}, repos.map(RepoItem)), - error: h('p', {}, text('Error')), - }), - ), -}); +fetchProjects();