From b818d0da05b9044de35970a8677f6b2638bc05d6 Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:43:00 -0400 Subject: [PATCH 01/34] refactor: remove old typeahead component files --- .../src/sass/scss/03-vendor/_typeahead.scss | 94 ------------ .../src/scripts/components/pattern-finder.js | 135 ------------------ .../src/scripts/components/typeahead.js | 7 - 3 files changed, 236 deletions(-) delete mode 100644 packages/uikit-workshop/src/sass/scss/03-vendor/_typeahead.scss delete mode 100644 packages/uikit-workshop/src/scripts/components/pattern-finder.js delete mode 100755 packages/uikit-workshop/src/scripts/components/typeahead.js diff --git a/packages/uikit-workshop/src/sass/scss/03-vendor/_typeahead.scss b/packages/uikit-workshop/src/sass/scss/03-vendor/_typeahead.scss deleted file mode 100644 index 97116926d..000000000 --- a/packages/uikit-workshop/src/sass/scss/03-vendor/_typeahead.scss +++ /dev/null @@ -1,94 +0,0 @@ -/*------------------------------------*\ - #TWITTER TYPEAHEAD -\*------------------------------------*/ -/** - * Using Twitter Typeahead to autocomplete pattern - * searches. https://twitter.github.io/typeahead.js/ - * 1) Wrapped in pl-c-header to contain styles for projects that - * might already be using Typeahead -*/ - -.pl-c-header { - /** - * Typeahead input - * 1) the primary DOM element that initiates the JS library - */ - .pl-c-typeahead { - border: 0; - background-color: $pl-color-gray-87 !important; - color: $pl-color-gray-50; - width: 100%; - right: 0; - padding: 0.61rem 0.5rem; - font-size: inherit; - - &:focus { - background-color: $pl-color-gray-50; - color: $pl-color-white; - } - } - - /** - * Typeahead wrapper - * 1) This is the JS-generated wrapper around the input - * 2) Display after nav list items for horizontal theme - */ - .twitter-typeahead { - display: flex !important; - order: 2; /* 2 */ - width: 100%; - } - - .tt-input { - background-color: $pl-color-gray-50; - color: $pl-color-white; - - &:hover { - color: $pl-color-white; - background-color: $pl-color-gray-87 !important; - - &::-webkit-input-placeholder { - color: $pl-color-white; - } - - &::-moz-input-placeholder { - color: $pl-color-white; - } - } - - &:focus { - border-radius: 0; - text-transform: lowercase; - color: $pl-color-white; - background-color: $pl-color-gray-87; - outline: 1px dotted $pl-color-gray-50; - outline-offset: -1px; - } - } - - .tt-hint { - text-transform: lowercase; - } - - .tt-menu { - text-transform: lowercase; - background-color: $pl-color-gray-50; - width: 100%; - border-bottom-right-radius: $pl-border-radius-med; - border-bottom-left-radius: $pl-border-radius-med; - } - - .tt-suggestion { - color: $pl-color-gray-07; - padding: 0.8em; - } - - .tt-suggestion.tt-cursor { - color: $pl-color-white; - background-color: $pl-color-trans-white-25; - } - - .tt-suggestion p { - margin: 0; - } -} diff --git a/packages/uikit-workshop/src/scripts/components/pattern-finder.js b/packages/uikit-workshop/src/scripts/components/pattern-finder.js deleted file mode 100644 index 521886d08..000000000 --- a/packages/uikit-workshop/src/scripts/components/pattern-finder.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Pattern Finder - */ - -import $ from 'jquery'; -import Bloodhound from 'typeahead.js/dist/bloodhound.js'; -import { urlHandler } from '../utils'; - -export const patternFinder = { - data: [], - active: false, - - init() { - // don't init more than once. - if (document.querySelectorAll('.pl-js-typeahead').length > 1) { - return; - } - - for (const patternType in window.patternPaths) { - if (window.patternPaths.hasOwnProperty(patternType)) { - for (const pattern in window.patternPaths[patternType]) { - if (window.patternPaths[patternType].hasOwnProperty(pattern)) { - const obj = {}; - obj.patternPartial = patternType + '-' + pattern; - obj.patternPath = window.patternPaths[patternType][pattern]; - this.data.push(obj); - } - } - } - } - - // instantiate the bloodhound suggestion engine - const patterns = new Bloodhound({ - datumTokenizer(d) { - return Bloodhound.tokenizers.nonword(d.patternPartial); - }, - queryTokenizer: Bloodhound.tokenizers.nonword, - limit: 10, - local: this.data, - }); - - // initialize the bloodhound suggestion engine - patterns.initialize(); - - $('.pl-js-typeahead') - .typeahead( - { - highlight: true, - }, - { - displayKey: 'patternPartial', - source: patterns.ttAdapter(), - } - ) - .on('typeahead:selected', patternFinder.onSelected) - .on('typeahead:autocompleted', patternFinder.onAutocompleted); - }, - - passPath(item) { - // update the iframe via the history api handler - patternFinder.closeFinder(); - const obj = JSON.stringify({ - event: 'patternLab.updatePath', - path: urlHandler.getFileName(item.patternPartial), - }); - document - .querySelector('.pl-js-iframe') - .contentWindow.postMessage(obj, urlHandler.targetOrigin); - }, - - onSelected(e, item) { - patternFinder.passPath(item); - }, - - onAutocompleted(e, item) { - patternFinder.passPath(item); - }, - - toggleFinder() { - if (!patternFinder.active) { - patternFinder.openFinder(); - } else { - patternFinder.closeFinder(); - } - }, - - openFinder() { - patternFinder.active = true; - $('.pl-js-typeahead').val(''); - }, - - closeFinder() { - patternFinder.active = false; - document.activeElement.blur(); - $('.pl-js-typeahead').val(''); - }, - - receiveIframeMessage(event) { - // does the origin sending the message match the current host? if not dev/null the request - if ( - window.location.protocol !== 'file:' && - event.origin !== window.location.protocol + '//' + window.location.host - ) { - return; - } - - let data = {}; - try { - data = - typeof event.data !== 'string' ? event.data : JSON.parse(event.data); - } catch (e) { - // @todo: how do we want to handle exceptions here? - } - - if (data.event !== undefined && data.event === 'patternLab.keyPress') { - if (data.keyPress === 'ctrl+shift+f') { - patternFinder.toggleFinder(); - } - } - }, -}; - -patternFinder.init(); - -window.addEventListener('message', patternFinder.receiveIframeMessage, false); - -$('.pl-js-typeahead').focus(function() { - if (!patternFinder.active) { - patternFinder.openFinder(); - } -}); - -$('.pl-js-typeahead').blur(function() { - patternFinder.closeFinder(); -}); diff --git a/packages/uikit-workshop/src/scripts/components/typeahead.js b/packages/uikit-workshop/src/scripts/components/typeahead.js deleted file mode 100755 index c3b0bdd4a..000000000 --- a/packages/uikit-workshop/src/scripts/components/typeahead.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Typeahead - Twitter's typeahead.js deps - */ - -import 'jquery'; -import 'typeahead.js/dist/typeahead.jquery.js'; -import { Bloodhound } from 'typeahead.js/dist/bloodhound.js'; From d09138408bd705758b2ed35ba78206f976e0abce Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:43:47 -0400 Subject: [PATCH 02/34] refactor: add new get-target-origin JavaScript helper defined and used in virtually every single component in Pattern Lab --- .../uikit-workshop/src/scripts/utils/get-target-origin.js | 4 ++++ packages/uikit-workshop/src/scripts/utils/index.js | 1 + 2 files changed, 5 insertions(+) create mode 100644 packages/uikit-workshop/src/scripts/utils/get-target-origin.js diff --git a/packages/uikit-workshop/src/scripts/utils/get-target-origin.js b/packages/uikit-workshop/src/scripts/utils/get-target-origin.js new file mode 100644 index 000000000..f2de25e59 --- /dev/null +++ b/packages/uikit-workshop/src/scripts/utils/get-target-origin.js @@ -0,0 +1,4 @@ +export const targetOrigin = + window.location.protocol === 'file:' + ? '*' + : window.location.protocol + '//' + window.location.host; diff --git a/packages/uikit-workshop/src/scripts/utils/index.js b/packages/uikit-workshop/src/scripts/utils/index.js index ec08e48d8..2e2a999c7 100755 --- a/packages/uikit-workshop/src/scripts/utils/index.js +++ b/packages/uikit-workshop/src/scripts/utils/index.js @@ -6,3 +6,4 @@ export { Dispatcher } from './eventemitter'; export { css } from './css'; export { extend } from './extend'; export { supportsShadowDom } from './supports-shadow-dom'; +export { targetOrigin } from './get-target-origin'; From 3df3afe1eb08685b7d65fbfe48de909f9204638d Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:44:19 -0400 Subject: [PATCH 03/34] chore: remove old typeahead dependency; add new autocomplete-related deps --- packages/uikit-workshop/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/uikit-workshop/package.json b/packages/uikit-workshop/package.json index 43bc4ace6..fdc2ad04c 100644 --- a/packages/uikit-workshop/package.json +++ b/packages/uikit-workshop/package.json @@ -69,6 +69,10 @@ "access": "public" }, "dependencies": { + "@reach/visually-hidden": "^0.1.1", + "react-html-parser": "^2.0.2", + "fuse.js": "^3.2.1", + "react-autocomplete": "^1.8.1", "classnames": "^2.2.6", "react-animate-height": "^2.0.4", "@skatejs/renderer-preact": "^0.3.3", @@ -96,7 +100,6 @@ "scriptjs": "^2.5.8", "scroll-js": "^1.9.1", "skatejs": "^5.2.4", - "typeahead.js": "^0.11.1", "whendefined": "^0.0.1", "wolfy87-eventemitter": "^5.2.4" } From 46851fbc2f0cc98b7623ecec7bb6b035f3a16a77 Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:47:38 -0400 Subject: [PATCH 04/34] refactor: remove old styleguide integration with the original typeahead search functionality; add new keyboard event binding for handling cross-iframe bindings --- .../pl-search/pl-search.iframe-helper.js | 23 +++++++++++++++++++ .../src/scripts/components/styleguide.js | 2 -- .../src/scripts/patternlab-pattern.js | 4 +--- 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 packages/uikit-workshop/src/scripts/components/pl-search/pl-search.iframe-helper.js diff --git a/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.iframe-helper.js b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.iframe-helper.js new file mode 100644 index 000000000..e60f9f984 --- /dev/null +++ b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.iframe-helper.js @@ -0,0 +1,23 @@ +// Tiny helper script to listen for keyboard combos and to communicate back to the main Search component (via the Pattern Lab iframe) +import Mousetrap from 'mousetrap'; +import { targetOrigin } from '../../utils'; + +Mousetrap.bind('command+f', function(e) { + e.preventDefault(); + + try { + const obj = JSON.stringify({ + event: 'patternLab.keyPress', + key: e.key, + altKey: e.altKey, + ctrlKey: e.ctrlKey, + metaKey: e.metaKey, + shiftKey: e.shiftKey, + }); + window.parent.postMessage(obj, targetOrigin); + } catch (error) { + // @todo: how do we want to handle exceptions here? + } + + return false; +}); diff --git a/packages/uikit-workshop/src/scripts/components/styleguide.js b/packages/uikit-workshop/src/scripts/components/styleguide.js index eb7cdd12b..9a9adfc7b 100644 --- a/packages/uikit-workshop/src/scripts/components/styleguide.js +++ b/packages/uikit-workshop/src/scripts/components/styleguide.js @@ -5,7 +5,6 @@ import $ from 'jquery'; import Mousetrap from 'mousetrap'; import { urlHandler, DataSaver } from '../utils'; -import { patternFinder } from './pattern-finder'; (function(w) { let sw = document.body.clientWidth; //Viewport Width @@ -572,7 +571,6 @@ import { patternFinder } from './pattern-finder'; $('.pl-js-nav-container, .pl-js-acc-handle, .pl-js-acc-panel').removeClass( 'pl-is-active' ); - patternFinder.closeFinder(); } // update the iframe with the source from clicked element in pull down menu. also close the menu diff --git a/packages/uikit-workshop/src/scripts/patternlab-pattern.js b/packages/uikit-workshop/src/scripts/patternlab-pattern.js index a2f98f128..a64dfed95 100755 --- a/packages/uikit-workshop/src/scripts/patternlab-pattern.js +++ b/packages/uikit-workshop/src/scripts/patternlab-pattern.js @@ -1,5 +1,3 @@ import './utils/postmessage'; import './components/modal-styleguide'; - -// WIP: new JS components for handling UI controls to toggle theme -// import './components/toggle-theme-sg'; +import './components/pl-search/pl-search.iframe-helper'; // communicates with the main component via the PL iframe From 945e4787287830f4f2cbf20eaa777574a74bc11a Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:48:27 -0400 Subject: [PATCH 05/34] refactor: add refreshed and consolidated styles for the refactored search component --- .../components/pl-search/pl-search.scss | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 packages/uikit-workshop/src/scripts/components/pl-search/pl-search.scss diff --git a/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.scss b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.scss new file mode 100644 index 000000000..0522cbbad --- /dev/null +++ b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.scss @@ -0,0 +1,286 @@ +/*------------------------------------*\ + #TWITTER TYPEAHEAD +\*------------------------------------*/ + +@import '../../../sass/scss/core.scss'; +/** + * Using Twitter Typeahead to autocomplete pattern + * searches. https://twitter.github.io/typeahead.js/ +*/ + +$pl-clear-button-size: 1.8rem; +$pl-clear-button-size-at-med: 1.5rem; + +pl-search { + background-color: inherit; + order: 2; // Display after nav list items + top: 0; + z-index: 10; + flex-shrink: 0; + padding: 0.3rem 0.5rem; + display: inline-block; + + @media screen and (min-width: $pl-bp-med){ + margin-left: 1rem; + flex-direction: row; + flex-shrink: 1; + + .pl-c-body--theme-sidebar & { + flex-direction: column; + margin-left: 0; + padding-left: 0; + padding-right: 0; + width: 100%; + } + } +} + +/** + * Typeahead wrapper + * 1) This is the JS-generated wrapper around the input + */ +.pl-c-typeahead { + width: 100%; + background-color: inherit; + order: 2; // Display after nav list items + display: flex !important; + z-index: 10; + text-transform: capitalize; + flex-direction: column; + color: $pl-color-gray-02; + + .pl-c-body--theme-light & { + color: $pl-color-gray-87; + } + + @media screen and (min-width: $pl-bp-med){ + flex-direction: row; + + .pl-c-body--theme-sidebar & { + flex-direction: column; + } + } +} + +.pl-c-typeahead__hint { + top: 0; + left: 0; + right: 0; + width: 100%; +} + +.pl-c-typeahead__hint, +.pl-c-typeahead__input { + text-transform: capitalize; + border-radius: $pl-border-radius-med; + background-color: $pl-color-gray-87; + color: $pl-color-white; + border-color: darken($pl-color-gray-87, 10%); + text-overflow: ellipsis; + border-width: 1px; + border-style: solid; + transition: all 0.1s ease; + max-width: 100%; + padding: 0.31rem 0.5rem; + font-size: 16px; // prevent zooming in on mobile + + .pl-c-body--theme-sidebar & { + border-radius: 0; + } + + // Modify the padding defaults when the clear button UI exists + &--with-clear-button { + padding-right: $pl-clear-button-size; + + @media all and (min-width: $pl-bp-med) { + padding-right: $pl-clear-button-size-at-med; + } + } + + @media all and (min-width: $pl-bp-med) { + max-width: 12vw; + font-size: inherit; + + .pl-c-body--theme-sidebar & { + max-width: none; + } + } + + .pl-c-body--theme-light & { + background-color: $pl-color-gray-07 !important; + color: $pl-color-gray-70 !important; + border-color: $pl-color-gray-13 !important; + } + + &::-webkit-input-placeholder, + &::-moz-input-placeholder { + color: $pl-color-white !important; + transition: all 0.1s ease; + } + + + &:hover, + &:focus { + color: $pl-color-white; + background-color: darken($pl-color-gray-87, 2%) !important; + + .pl-c-body--theme-light & { + color: $pl-color-gray-87 !important; + background-color: $pl-color-gray-13 !important; + border-color: $pl-color-gray-20 !important; + } + + &::-moz-input-placeholder, + &::-webkit-input-placeholder { + color: $pl-color-white !important; + + .pl-c-body--theme-light & { + color: $pl-color-gray-87 !important; + } + } + } +} + +.pl-c-typeahead__menu { + @include accordionPanel; + background-color: $pl-color-gray-87; + border-bottom-right-radius: $pl-border-radius-med; + border-bottom-left-radius: $pl-border-radius-med; + text-transform: capitalize; + min-width: 100%; + width: 100%; + overflow: hidden; + right: 0; + max-height: 0; + display: block !important; + transition: max-height 0.3s ease, opacity 0.3s ease; + opacity: 0; + + .pl-c-body--theme-light & { + background-color: $pl-color-gray-02; + } + + &.pl-is-open:not(.pl-is-empty) { + max-height: 120rem; + max-height: calc(var(--viewport-height) - 4rem); + opacity: 1; + } + + .pl-c-body--theme-sidebar & { + @media all and (min-width: $pl-bp-med) { + position: relative !important; + border-radius: 0; + } + } + + @media all and (max-width: $pl-bp-med - 1) { + position: relative !important; + } +} + +.pl-c-typeahead__results { + background-color: inherit; + border-color: transparent; + border-width: 1px; + border-style: solid; + border-bottom-right-radius: $pl-border-radius-med; + border-bottom-left-radius: $pl-border-radius-med; + overflow: hidden; + border-color: darken($pl-color-gray-87, 5%); + + &:empty { + border-width: 0; + max-height: 0; + } + + .pl-c-body--theme-light & { + border-color: $pl-color-gray-07; + } + + .pl-c-body--theme-sidebar & { + @media all and (min-width: $pl-bp-med) { + border-radius: 0; + } + } +} + +.pl-c-typeahead__result { + transition: all 0.3s ease; + background-color: inherit; + padding: 0.8em; + cursor: pointer; + + &:hover { + background-color: rgba($pl-color-white, 0.15); + + .pl-c-body--theme-light & { + background-color: $pl-color-gray-07; + } + } + + &:active, + &:focus { + background-color: rgba(255, 255, 255, 0.18); + } +} + +.pl-c-typeahead__result.pl-has-cursor { + color: $pl-color-white; + background-color: $pl-color-trans-white-25; + + .pl-c-body--theme-light & { + color: $pl-color-black; + background-color: $pl-color-gray-13; + } +} + +.pl-c-typeahead__result p { + margin: 0; +} + +.pl-c-typeahead-wrapper { + position: relative; +} + +.pl-c-typeahead__clear-button { + @include linkStyle; + background-color: transparent; + height: $pl-clear-button-size; + width: $pl-clear-button-size; + + @media all and (min-width: $pl-bp-med) { + height: $pl-clear-button-size-at-med; + width: $pl-clear-button-size-at-med; + } + + .pl-c-body--theme-light & { + background-color: transparent; + } + + border-radius: 20rem; + overflow: hidden; + position: absolute; + right: 0; + top: 0; + z-index: 100; + cursor: pointer; + border: 0; + transition: opacity 0.1s ease; + opacity: 0; + visibility: hidden; + + &.pl-is-visible { + opacity: 1; + visibility: visible; + } +} + +.pl-c-typeahead__clear-button-icon { + fill: currentColor; + line-height: 0; + font-size: 0; + position: absolute; + top: 50%; + left: 50%; + transform: translate3d(-50%, -50%, 0); +} From ea20f7a75d216654969aea17f3245b4d6a9ab29f Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:49:28 -0400 Subject: [PATCH 06/34] refactor: add new react-autocomplete powered web component implementation of the search component --- .../scripts/components/pl-search/pl-search.js | 293 ++++++++++++++++++ .../src/scripts/patternlab-viewer.js | 3 +- 2 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js diff --git a/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js new file mode 100644 index 000000000..2a2b3af93 --- /dev/null +++ b/packages/uikit-workshop/src/scripts/components/pl-search/pl-search.js @@ -0,0 +1,293 @@ +import { define, props } from 'skatejs'; +import { h } from 'preact'; +import Fuse from 'fuse.js'; +import ReactHtmlParser from 'react-html-parser'; +import classNames from 'classnames'; +import Mousetrap from 'mousetrap'; + +import VisuallyHidden from '@reach/visually-hidden'; +import ReactAutocomplete from 'react-autocomplete'; + +import { urlHandler } from '../../utils'; +import { BaseComponent } from '../base-component'; + +@define +class Search extends BaseComponent { + static is = 'pl-search'; + + constructor(self) { + self = super(self); + this.useShadow = false; + this.defaultMaxResults = 10; + + this.state = { + isOpen: false, + value: '', + }; + + this.receiveIframeMessage = this.receiveIframeMessage.bind(this); + this.toggleSearch = this.toggleSearch.bind(this); + this.clearSearch = this.clearSearch.bind(this); + this.closeSearch = this.closeSearch.bind(this); + this.openSearch = this.openSearch.bind(this); + + this.data = []; + for (const patternType in window.patternPaths) { + if (window.patternPaths.hasOwnProperty(patternType)) { + for (const pattern in window.patternPaths[patternType]) { + if (window.patternPaths[patternType].hasOwnProperty(pattern)) { + const obj = {}; + obj.label = patternType + '-' + pattern; + obj.id = window.patternPaths[patternType][pattern]; + this.data.push(obj); + } + } + } + } + + return self; + } + + connected() { + const self = this; + Mousetrap.bind('command+f', function(e) { + e.preventDefault(); + self.toggleSearch(); + }); + window.addEventListener('message', this.receiveIframeMessage, false); + } + + rendered() { + this.inputElement = this.input; + } + + static props = { + maxResults: props.string, + placeholder: props.string, + showClearButton: props.boolean, + clearButtonText: props.string, + }; + + // External Redux store not yet in use + _stateChanged(state) {} + + // update the iframe via the history api handler + passPath(item) { + this.setState({ value: item }); + this.closeSearch(); + const obj = JSON.stringify({ + event: 'patternLab.updatePath', + path: urlHandler.getFileName(item), + }); + document + .querySelector('.pl-js-iframe') + .contentWindow.postMessage(obj, urlHandler.targetOrigin); + } + + toggleSearch() { + if (!this.state.isOpen) { + this.openSearch(); + } else { + this.closeSearch(); + } + } + + clearSearch() { + this.inputElement.focus(); + this.setState({ + value: '', + }); + } + + openSearch() { + this.inputElement.focus(); + } + + closeSearch() { + document.activeElement.blur(); + } + + receiveIframeMessage(event) { + // does the origin sending the message match the current host? if not dev/null the request + if ( + window.location.protocol !== 'file:' && + event.origin !== window.location.protocol + '//' + window.location.host + ) { + return; + } + + let data = {}; + try { + data = + typeof event.data !== 'string' ? event.data : JSON.parse(event.data); + } catch (e) { + // @todo: how do we want to handle exceptions here? + } + + if (data.event !== undefined && data.event === 'patternLab.keyPress') { + if (data.key === 'f' && data.metaKey === true) { + this.toggleSearch(); + } + } + } + + // highlights keywords in the search results in a react-friendly way + limits total number / max displayed + filterAndLimitResults() { + const data = this.data; + + const maxResults = this.props.maxResults + ? this.props.maxResults + : this.defaultMaxResults; + + const fuseOptions = { + shouldSort: true, + threshold: 0.2, + tokenize: true, + includeMatches: true, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: ['label'], + }; + const fuse = new Fuse(data, fuseOptions); + const results = fuse.search(this.state.value ? this.state.value : ''); + + const highlighter = function(item) { + const resultItem = item; + resultItem.matches.forEach(matchItem => { + const text = resultItem.item[matchItem.key]; + const result = []; + const matches = [].concat(matchItem.indices); + let pair = matches.shift(); + + for (let i = 0; i < text.length; i++) { + const char = text.charAt(i); + if (pair && i === pair[0]) { + result.push(''); + } + result.push(char); + if (pair && i === pair[1]) { + result.push(''); + pair = matches.shift(); + } + } + resultItem.item.highlightedLabel = result.join(''); + + resultItem.item.highlightedLabel = ReactHtmlParser( + resultItem.item.highlightedLabel + ); + + if (resultItem.children && resultItem.children.length > 0) { + resultItem.children.forEach(child => { + highlighter(child); + }); + } + }); + }; + + results.forEach(resultItem => { + highlighter(resultItem); + }); + + const reducedResults = results.reduce((total, result) => { + total.push(result.item); + return total; + }, []); + + if (reducedResults.length < maxResults) { + return reducedResults; + } else { + return reducedResults.slice(0, maxResults); + } + } + + render() { + const open = this.state.isOpen; + const currentValue = this.state.value; + const shouldShowClearButton = this.props.showClearButton + ? this.props.showClearButton + : true; + + const clearButtonText = this.props.clearButtonText + ? this.props.clearButtonText + : 'Clear Search Results'; + + return ( +
+ (this.input = el)} + wrapperProps={{ + className: classNames('pl-c-typeahead'), + }} + //setting autoHighlight to false seems to help prevent an occasional JS error from firing (relating to the dom-scroll-into-view library) + autoHighlight={false} + onMenuVisibilityChange={isOpen => this.setState({ isOpen })} + getItemValue={item => item.label} + renderItem={(item, highlighted) => ( +
+ {item.highlightedLabel} +
+ )} + inputProps={{ + className: classNames('pl-c-typeahead__input', { + [`pl-c-typeahead__input--with-clear-button`]: shouldShowClearButton, + }), + placeholder: this.props.placeholder + ? this.props.placeholder + : 'Find a Pattern', + }} + renderMenu={(items, value, style) => ( +
+
+
+ )} + value={this.state.value} + onChange={e => { + e.target.value !== '' && e.target.value !== undefined + ? this.setState({ value: e.target.value }) + : this.setState({ value: '' }); + }} + onSelect={value => this.passPath(value)} + /> + {shouldShowClearButton && ( + + )} +
+ ); + } +} + +export { Search }; diff --git a/packages/uikit-workshop/src/scripts/patternlab-viewer.js b/packages/uikit-workshop/src/scripts/patternlab-viewer.js index 9511e5cf8..af4f21667 100755 --- a/packages/uikit-workshop/src/scripts/patternlab-viewer.js +++ b/packages/uikit-workshop/src/scripts/patternlab-viewer.js @@ -10,12 +10,11 @@ loadPolyfills.then(res => { ); import(/* webpackMode: 'eager', webpackChunkName: 'pl-toggle-theme' */ './components/pl-toggle-theme/pl-toggle-theme'); import(/* webpackMode: 'eager', webpackChunkName: 'pl-toggle-layout' */ './components/pl-toggle-layout/pl-toggle-layout'); + import(/* webpackMode: 'eager', webpackChunkName: 'pl-search' */ './components/pl-search/pl-search'); }); -import './components/typeahead'; import './components/panels'; import './components/panels-viewer'; -import './components/pattern-finder'; import './components/plugin-loader'; //// Add hook to auto re-render the root component. From edb9bf5b514a2c91aaabd1dcc7354eafd524e75c Mon Sep 17 00:00:00 2001 From: Salem Ghoweri Date: Sun, 23 Sep 2018 13:50:37 -0400 Subject: [PATCH 07/34] chore: wire up template integration with refactored search component; update Sass to pull in component-specific styling till Shadow DOM integration worked on --- packages/uikit-workshop/src/html/partials/header.html | 3 ++- packages/uikit-workshop/src/sass/pattern-lab.scss | 2 +- .../src/sass/scss/05-themes/_sidebar-theme.scss | 4 ---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/uikit-workshop/src/html/partials/header.html b/packages/uikit-workshop/src/html/partials/header.html index 3f94d6f11..68be128dc 100644 --- a/packages/uikit-workshop/src/html/partials/header.html +++ b/packages/uikit-workshop/src/html/partials/header.html @@ -4,7 +4,8 @@