From d74f3a1f15b2fe87cde872eb972212d014f3de12 Mon Sep 17 00:00:00 2001 From: Fynn Becker Date: Fri, 10 Nov 2017 11:33:25 +0100 Subject: [PATCH] Add fuzzy search to sidebar --- docs/css/index.scss | 1 + docs/css/scaffolding.scss | 16 ++++ docs/css/sidebar-toggle.scss | 72 ++++++++++++++++ docs/css/sidebar.scss | 158 ++++++++++++++++++----------------- docs/css/variables.scss | 2 +- docs/js/index.js | 1 + docs/js/search.js | 70 ++++++++++++++++ docs/js/sidebar.js | 2 +- docs/templates/sidebar.njk | 44 +++++++++- package.json | 1 + yarn.lock | 4 + 11 files changed, 289 insertions(+), 82 deletions(-) create mode 100644 docs/css/sidebar-toggle.scss create mode 100644 docs/js/search.js diff --git a/docs/css/index.scss b/docs/css/index.scss index ec9917cc..ce345bdd 100644 --- a/docs/css/index.scss +++ b/docs/css/index.scss @@ -8,3 +8,4 @@ @import "header"; @import "section"; @import "sidebar"; +@import "sidebar-toggle"; diff --git a/docs/css/scaffolding.scss b/docs/css/scaffolding.scss index e808f833..c4db1267 100644 --- a/docs/css/scaffolding.scss +++ b/docs/css/scaffolding.scss @@ -1,3 +1,19 @@ +.fesg-sr-only { + position: absolute; + + width: 1px; + height: 1px; + margin: -1px; + border: 0; + padding: 0; + + white-space: nowrap; + + clip-path: inset(100%); + clip: rect(0 0 0 0); + overflow: hidden; +} + .fesg-content { @include fesg-reset-pseudo; diff --git a/docs/css/sidebar-toggle.scss b/docs/css/sidebar-toggle.scss new file mode 100644 index 00000000..45011ba5 --- /dev/null +++ b/docs/css/sidebar-toggle.scss @@ -0,0 +1,72 @@ +.fesg-sidebar-toggle { + @include fesg-reset; + + position: fixed; + top: 0; + left: ($fesg-sidebar-width - 0.125rem); + width: 2rem; + height: 2rem; + padding: 0; + transform: translateX(-$fesg-sidebar-width); + + z-index: ($fesg-z-index-sidebar + 1); + + background: $fesg-color-sidebar-button-background; + background: var(--fesg-color-sidebar-button-background, var(--fesg-color-primary, $fesg-color-sidebar-button-background)); + border: none; + border-radius: 0; + + cursor: pointer; + + &.is-active { + transform: translateX(0); + } + + &.is-animated { + transition: transform $fesg-transition-sidebar; + } +} + +.fesg-sidebar-toggle__line { + @include fesg-reset; + + position: absolute; + top: calc(50% - 0.0625rem); + left: 20%; + height: 0.125rem; + + background: $fesg-color-sidebar-button-foreground; + background: var(--fesg-color-sidebar-button-foreground, $fesg-color-sidebar-button-foreground); + border-radius: 0.0625rem; + + transition: + transform $fesg-transition-sidebar, + opacity $fesg-transition-sidebar; + + &:nth-child(1) { + width: 60%; + transform: translateY(-0.375rem); + + .is-active & { + transform: rotate(45deg); + } + } + + &:nth-child(2) { + width: 45%; + + .is-active & { + opacity: 0; + } + } + + &:nth-child(3) { + width: 55%; + transform: translateY(0.375rem); + + .is-active & { + width: 60%; + transform: rotate(-45deg); + } + } +} diff --git a/docs/css/sidebar.scss b/docs/css/sidebar.scss index fde9af36..55f21ac2 100644 --- a/docs/css/sidebar.scss +++ b/docs/css/sidebar.scss @@ -1,140 +1,146 @@ -.fesg-sidebar-toggle { +.fesg-sidebar { @include fesg-reset; position: fixed; top: 0; - left: ($fesg-sidebar-width - 0.125rem); - width: 2rem; - height: 2rem; - padding: 0; - transform: translateX(-$fesg-sidebar-width); + left: 0; + width: $fesg-sidebar-width; + height: 100%; + padding: $fesg-sidebar-padding 0; + transform: translateX(-100%); - z-index: ($fesg-z-index-sidebar + 1); + z-index: $fesg-z-index-sidebar; - background: $fesg-color-sidebar-button-background; - background: var(--fesg-color-sidebar-button-background, var(--fesg-color-primary, $fesg-color-sidebar-button-background)); - border: none; - border-radius: 0; + background: $fesg-color-sidebar-background; + background: var(--fesg-color-sidebar-background, $fesg-color-sidebar-background); + border-right: 0.125rem solid; + border-right-color: $fesg-color-sidebar-border; + border-right-color: var(--fesg-color-sidebar-border, var(--fesg-color-border, $fesg-color-sidebar-border)); - cursor: pointer; + overflow-y: auto; - &.is-active { + &.is-open { transform: translateX(0); + box-shadow: 0 0 2rem rgba(0, 0, 0, 0.2); } &.is-animated { transition: transform $fesg-transition-sidebar; } + + @media (min-width: $fesg-breakpoint-sidebar) { + &.is-open { + box-shadow: none; + } + } } -.fesg-sidebar-toggle__line { +.fesg-sidebar__form { @include fesg-reset; - position: absolute; - top: calc(50% - 0.0625rem); - left: 20%; - height: 0.125rem; - - background: $fesg-color-sidebar-button-foreground; - background: var(--fesg-color-sidebar-button-foreground, $fesg-color-sidebar-button-foreground); - border-radius: 0.0625rem; - - transition: - transform $fesg-transition-sidebar, - opacity $fesg-transition-sidebar; + position: relative; + margin: 0 $fesg-sidebar-padding; +} - &:nth-child(1) { - width: 60%; - transform: translateY(-0.375rem); +.fesg-sidebar__search { + @include fesg-reset; - .is-active & { - transform: rotate(45deg); - } - } + display: block; + width: 100%; + padding: ($fesg-sidebar-padding / 1.5) ($fesg-sidebar-padding + 1.5rem) ($fesg-sidebar-padding / 1.5) $fesg-sidebar-padding; - &:nth-child(2) { - width: 45%; + border: 0.0625rem solid; + border-color: $fesg-color-sidebar-border; + border-color: var(--fesg-color-sidebar-border, var(--fesg-color-border, $fesg-color-sidebar-border)); +} - .is-active & { - opacity: 0; - } - } +.fesg-sidebar__search-reset { + @include fesg-reset; - &:nth-child(3) { - width: 55%; - transform: translateY(0.375rem); + display: none; + position: absolute; + width: 1.25rem; + height: 1.25rem; + top: 50%; + right: ($fesg-sidebar-padding / 1.5); - .is-active & { - width: 60%; - transform: rotate(-45deg); - } - } -} + transform: translateY(-50%); -.fesg-sidebar { - @include fesg-reset; + border: 0.0625rem solid; + border-color: $fesg-color-sidebar-button-background; + border-color: var(--fesg-color-sidebar-button-background, var(--fesg-color-primary, $fesg-color-sidebar-button-background)); + border-radius: 50%; - position: fixed; - top: 0; - left: 0; - width: $fesg-sidebar-width; - height: 100%; - padding: $fesg-sidebar-padding 0; - transform: translateX(-100%); + cursor: pointer; - z-index: $fesg-z-index-sidebar; + &.is-visible { + display: block; + } - background: $fesg-color-sidebar-background; - background: var(--fesg-color-sidebar-background, $fesg-color-sidebar-background); - border-right: 0.125em solid; - border-right-color: $fesg-color-sidebar-border; - border-right-color: var(--fesg-color-sidebar-border, var(--fesg-color-border, $fesg-color-sidebar-border)); + &::before, + &::after { + content: ""; - overflow-y: auto; + display: block; + position: absolute; + width: 80%; + height: 0.0625rem; + top: 50%; + left: 50%; - &.is-open { - transform: translateX(0); - box-shadow: 0 0 2rem rgba(0, 0, 0, 0.2); + background: $fesg-color-sidebar-button-background; + background: var(--fesg-color-sidebar-button-background, var(--fesg-color-primary, $fesg-color-sidebar-button-background)); } - &.is-animated { - transition: transform $fesg-transition-sidebar; + &::before { + transform: translate(-50%, -50%) rotate(45deg); } - @media (min-width: $fesg-breakpoint-sidebar) { - &.is-open { - box-shadow: none; - } + &::after { + transform: translate(-50%, -50%) rotate(-45deg); } } .fesg-sidebar__section { @include fesg-reset; - margin-bottom: 1.25rem; + margin-bottom: 1.5rem; &:last-child { margin-bottom: 0; } } -.fesg-sidebar__title { +.fesg-sidebar__title, +.fesg-sidebar__heading { @include fesg-reset; - @include fesg-fluid-type(1rem, 1.125rem); margin: 0 $fesg-sidebar-padding 0.5rem; + font-size: 1.125rem; font-weight: 600; color: $fesg-color-sidebar-text; color: var(--fesg-color-sidebar-text, var(--fesg-color-text, $fesg-color-sidebar-text)); } +.fesg-sidebar__heading--title { + font-size: 1.25rem; +} + .fesg-sidebar__list { @include fesg-reset; list-style: none; } +.fesg-sidebar__item { + @include fesg-reset; +} + +.fesg-sidebar__item--empty { + padding: 0.5rem $fesg-sidebar-padding; +} + .fesg-sidebar__link { @include fesg-reset; diff --git a/docs/css/variables.scss b/docs/css/variables.scss index 947283bc..b00ad2f1 100644 --- a/docs/css/variables.scss +++ b/docs/css/variables.scss @@ -30,7 +30,7 @@ $fesg-color-footer-border: $fesg-color-border; $fesg-spacing: 2vw; // Sidebar -$fesg-sidebar-width: 12.5rem; +$fesg-sidebar-width: 14rem; $fesg-sidebar-padding: 0.75rem; // Breakpoints diff --git a/docs/js/index.js b/docs/js/index.js index ac55690d..e96ed204 100644 --- a/docs/js/index.js +++ b/docs/js/index.js @@ -1,4 +1,5 @@ import './button' +import './search' import './section' import './sidebar' diff --git a/docs/js/search.js b/docs/js/search.js new file mode 100644 index 00000000..468ce94d --- /dev/null +++ b/docs/js/search.js @@ -0,0 +1,70 @@ +import Fuse from 'fuse.js' + +const searchForm = document.querySelector('.js-fesg-sidebar-form') +const searchInput = document.querySelector('.js-fesg-sidebar-search') +const searchReset = document.querySelector('.js-fesg-sidebar-search-reset') + +const componentsContainer = document.querySelector('.js-fesg-sidebar-components') +const prototypesContainer = document.querySelector('.js-fesg-sidebar-prototypes') + +const pageData = JSON.parse(document.querySelector('.js-fesg-sidebar-page-data').innerHTML) +const fuseOptions = { + keys: ['name'], + threshold: 0.4 +} + +const fuse = new Fuse(pageData, fuseOptions) + +searchForm.addEventListener('submit', event => { + event.preventDefault() +}) + +/** + * Create sidebar item HTML + * @param {Object} page Page object + */ +function createSidebarItem (page) { + return ` +
  • + + ${page.name} + +
  • ` +} + +/** + * Inject sidebar item HTML into sidebar + * @param {Array} list Page list + */ +function injectHTML (list) { + componentsContainer.innerHTML = list + .filter(item => item.type === 'component') + .map(createSidebarItem) + .join('') || '
  • No components found
  • ' + + prototypesContainer.innerHTML = list + .filter(item => item.type === 'prototype') + .map(createSidebarItem) + .join('') || '
  • No prototypes found
  • ' +} + +searchInput.addEventListener('input', event => { + let result = fuse.search(event.target.value) + + if (event.target.value) { + searchReset.classList.add('is-visible') + injectHTML(result) + } else { + searchReset.classList.remove('is-visible') + injectHTML(pageData) + } +}) + +searchReset.addEventListener('click', () => { + searchReset.classList.remove('is-visible') + searchInput.value = '' + searchInput.focus() + injectHTML(pageData) +}) diff --git a/docs/js/sidebar.js b/docs/js/sidebar.js index dda3cbe9..72316f39 100644 --- a/docs/js/sidebar.js +++ b/docs/js/sidebar.js @@ -1,7 +1,7 @@ const sidebar = document.querySelector('.js-fesg-sidebar') const button = document.querySelector('.js-fesg-sidebar-toggle') const content = document.querySelector('.js-fesg-content') -const breakpoint = window.matchMedia('(min-width: 62.5rem)') +const breakpoint = window.matchMedia('(min-width: 70rem)') /** * Open sidebar diff --git a/docs/templates/sidebar.njk b/docs/templates/sidebar.njk index e700050e..c9f2205d 100644 --- a/docs/templates/sidebar.njk +++ b/docs/templates/sidebar.njk @@ -12,8 +12,23 @@