diff options
| author | Armand Philippot <git@armandphilippot.com> | 2021-10-25 16:22:49 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2021-10-25 16:22:49 +0200 |
| commit | 58f7b1be7c1ce366eabae3b172de732f0122776b (patch) | |
| tree | 04f6c72e7f9af07a38491303c394269395f63da8 /htdocs/src/js/app.js | |
| parent | 7f8f54202834241ed6a859c3dc8349c6ee02742a (diff) | |
chore: print the project preview & details on click
On small viewport, hide the project details and display it when the
user clicks on toolbar button. So I adjust the toolbar z-index and the
preview height.
Diffstat (limited to 'htdocs/src/js/app.js')
| -rw-r--r-- | htdocs/src/js/app.js | 257 |
1 files changed, 216 insertions, 41 deletions
diff --git a/htdocs/src/js/app.js b/htdocs/src/js/app.js index 9a45242..636eb20 100644 --- a/htdocs/src/js/app.js +++ b/htdocs/src/js/app.js @@ -2,52 +2,76 @@ import projects from './config/projects'; import { isSmallVw, isStyleJsExists } from './utilities/helpers'; /** - * Hide the header and footer. - * @param {HTMLElement} header - The header element. - * @param {HTMLElement} footer - The footer element. - */ -function hideHeaderFooter(header, footer) { - header.classList.remove('slide-in--left'); - footer.classList.remove('slide-in--left'); - header.classList.add('slide-out--left'); - footer.classList.add('slide-out--left'); + * Change the element classes to hide it with a slide out left animation. + * @param {HTMLElement} el - The HTMLElement to hide. + */ +function hideToLeft(el) { + el?.classList.remove('slide-in--left'); + el?.classList.add('slide-out--left'); setTimeout(() => { - header.classList.add('hide'); - footer.classList.add('hide'); + el?.classList.add('hide'); }, 800); } /** - * Show the header and footer. - * @param {HTMLElement} header - The header element. - * @param {HTMLElement} footer - The footer element. + * Change the element classes to show it with a slide in left animation. + * @param {HTMLElement} el - The HTMLElement to show. */ -function showHeaderFooter(header, footer) { - header.classList.remove('slide-out--left'); - footer.classList.remove('slide-out--left'); - header.classList.remove('hide'); - footer.classList.remove('hide'); - header.classList.add('slide-in--left'); - footer.classList.add('slide-in--left'); +function showFromLeft(el) { + el?.classList.remove('slide-out--left'); + el?.classList.remove('hide'); + el?.classList.add('slide-in--left'); } /** - * Handle header and footer visibility. - * @returns {void} + * Show/hide header and footer with slide animation (left). */ function toggleHeaderFooter() { const header = document.querySelector('header'); const footer = document.querySelector('footer'); + const elements = [header, footer]; - if (!isSmallVw()) { - showHeaderFooter(header, footer); - return; - } + elements.forEach((el) => { + if (el.classList.contains('hide')) { + showFromLeft(el); + } else { + hideToLeft(el); + } + }); +} + +/** + * Change the element classes to hide it with a slide out bottom animation. + * @param {HTMLElement} el - The HTMLElement to hide. + */ +function hideToBottom(el) { + el?.classList.remove('slide-in--up'); + el?.classList.add('slide-out--bottom'); + setTimeout(() => { + el?.classList.add('hide'); + }, 800); +} + +/** + * Change the element classes to show it with a slide in up animation. + * @param {HTMLElement} el - The HTMLElement to show. + */ +function showFromBottom(el) { + el?.classList.remove('slide-out--bottom'); + el?.classList.remove('hide'); + el?.classList.add('slide-in--up'); +} + +/** + * Show/hide project details with slide animation (bottom). + */ +function toggleProjectDetails() { + const details = document.querySelector('.project-details'); - if (header.classList.contains('hide')) { - showHeaderFooter(header, footer); + if (details.classList.contains('hide')) { + showFromBottom(details); } else { - hideHeaderFooter(header, footer); + hideToBottom(details); } } @@ -60,30 +84,176 @@ function listenMenuBtn() { } /** - * Display or hide the toolbar depending on the current viewport width. + * Update the visibility of some DOM elements depending on viewport. */ -function toggleToolbar() { +function updateView() { + const header = document.querySelector('header'); + const footer = document.querySelector('footer'); const toolbar = document.querySelector('.toolbar'); + const details = document.querySelector('.project-details'); + if (isSmallVw()) { - toolbar.style.display = ''; + header.classList.add('hide'); + footer.classList.add('hide'); + toolbar.classList.remove('hide'); + details?.classList.add('hide'); + details?.classList.remove('fade-in'); } else { - toolbar.style.display = 'none'; + showFromLeft(header); + showFromLeft(footer); + toolbar.classList.add('hide'); + details?.classList.remove('hide'); + details?.classList.add('fade-in'); } } /** - * Change the visibility of some DOM elements. + * Update view when the window size changes. */ -function updateView() { - toggleToolbar(); - toggleHeaderFooter(); +function listenWindowSize() { + window.addEventListener('resize', updateView); } /** - * Update view when the window size changes. + * Retrieve a project by id. + * @param {Integer} id - The project id. + * @returns {Object} The current project. */ -function listenWindowSize() { - window.addEventListener('resize', updateView); +function getCurrentProject(id) { + return projects.find((project) => project.id === id); +} + +/** + * Get a list item for the given repo. + * @param {String} name - The repository name. + * @param {String} url - The repository URL. + * @returns {HTMLElement} A list item. + */ +function getRepoItem(name, url) { + const item = document.createElement('li'); + const link = document.createElement('a'); + const span = document.createElement('span'); + span.classList.add('screen-reader-text'); + span.textContent = name; + link.classList.add('list__link', `list__link--${name.toLocaleLowerCase()}`); + link.href = url; + link.appendChild(span); + item.classList.add('list__item'); + item.appendChild(link); + return item; +} + +/** + * Get the repos list wrapped inside ul element and the title. + * @param {Object[]} repos - An array of repo with name and URL. + * @returns {[title, list]} An array of HTMLElements for title and list. + */ +function getRepos(repos) { + if (repos.length === 0) return []; + + const wrapper = document.createElement('div'); + const title = document.createElement('h3'); + const list = document.createElement('ul'); + const items = repos.map((repo) => getRepoItem(repo.name, repo.url)); + title.classList.add('project-details__title'); + title.textContent = 'Repositories:'; + list.classList.add('list', 'list--repos'); + list.append(...items); + wrapper.append(title, list); + return [title, list]; +} + +/** + * Get the technologies list wrapped inside ul element and the title. + * @param {String[]} technologies - An array of technology names. + * @returns {[title, list]} An array of HTMLElements for title and list. + */ +function getTechs(technologies) { + if (technologies.length === 0) return []; + + const title = document.createElement('h3'); + title.classList.add('project-details__title'); + title.textContent = 'Technologies:'; + const list = document.createElement('ul'); + const items = technologies.map((technology) => { + const item = document.createElement('li'); + item.textContent = technology; + return item; + }); + list.classList.add('list', 'list--tech'); + list.append(...items); + return [title, list]; +} + +/** + * Retrieve the project details. + * @param {Object} project - The project. + * @returns {HTMLElement} The project details wrapped in a div. + */ +function getProjectDetails(project) { + const details = document.createElement('div'); + const title = document.createElement('h2'); + const techList = project?.technologies ? getTechs(project.technologies) : []; + const reposList = getRepos(project.repo); + let description; + + if (project.description) { + description = document.createElement('p'); + description.textContent = project.description; + } else { + description = ''; + } + + title.classList.add('project-details__title'); + title.textContent = `About ${project.name}`; + details.classList.add('project-details'); + details.replaceChildren(title, description, ...techList, ...reposList); + + return details; +} + +/** + * Get an iframe for the given path/url. + * @param {String} src - The path/url to use as source. + * @returns {HTMLElement} The iframe. + */ +function getIframe(src) { + const iframe = document.createElement('iframe'); + iframe.src = src; + return iframe; +} + +/** + * Retrieve the project preview. + * @param {String} projectPath - The project path. + * @returns {HTMLElement} The project preview wrapped in a div. + */ +function getProjectPreview(projectPath) { + const preview = document.createElement('div'); + const iframe = getIframe(projectPath); + preview.classList.add('project-preview', 'fade-in'); + preview.replaceChildren(iframe); + return preview; +} + +/** + * Display the targeted project. + * @param {String} id - The project id. + * @param {String} href - The project URL. + */ +function showProject(id, href) { + const currentProject = getCurrentProject(id); + const main = document.querySelector('.main'); + const details = getProjectDetails(currentProject); + const preview = getProjectPreview(currentProject.path); + const detailsBtn = document.querySelector('.btn--details'); + + if (isSmallVw()) details.classList.add('hide'); + + detailsBtn.textContent = `About ${currentProject.name}`; + detailsBtn.addEventListener('click', toggleProjectDetails); + window.history.pushState({}, currentProject.name, href); + main.replaceChildren(preview, details); } /** @@ -99,6 +269,11 @@ function getProjectsNavItem(id, name) { link.href = id; link.id = id; link.textContent = name; + link.addEventListener('click', (e) => { + e.preventDefault(); + showProject(id, e.target.href); + if (isSmallVw()) toggleHeaderFooter(); + }); item.classList.add('nav__item'); item.appendChild(link); return item; |
