Skip to content

Commit

Permalink
feat(2.0) v-model, slots, perf, etc. (#93)
Browse files Browse the repository at this point in the history
* feat(2.0) add more slots sections
  - adjust storybook + app demo + add tests
  - add back outline for focus in demos

* feat(2.0) v-model
  - use v-model and remove internal searchInput watcher
  - switch to input event handler for v-model support
  - address outstanding TODOs for major release breaking changes
    * remove deprecated events onInputChange, onClick, onBlur, onFocus,
      onSelected
    * remove class, name defaults
  - remove initialValue as now it can be set via v-model
  - docs(demo) update demo to use v-model

* feat(input) 'type' to be configurable via inputProps
* feat(autosuggest) shouldRendersuggestions to be more sane/flexible
* feat(slots) rename header/footer, add input slots

* refactor(computed) move sections and size to computed props. It didn't
make much sense to have the computedSections being calculated manually
so now it is a computed prop. This helps with perf since computed props
have memoization. This also allows to also remove computedSize so we can
remove side effects and difficult to understand computations. This also
completely removes the watcher, which is something that has needed to
happen since I introduced it.
* refactor(getSize) rename getSize to computed prop totalResults
* refactor(*) Remove dead code identified by test coverage

* fix(initialValue) since we removed initialValue, make v-model work.

* docs(readme) update readme to have all the new stuff v-model, etc
* docs(storybook) update stories to match new feats
* docs(readme) beef up the readme advanced example
  - add demo to show flexibility / no results example
  • Loading branch information
darrenjennings committed May 17, 2019
1 parent 7e59039 commit 458f3e2
Show file tree
Hide file tree
Showing 15 changed files with 14,566 additions and 1,197 deletions.
31 changes: 19 additions & 12 deletions .storybook/preview-head.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
}

#autosuggest__input {
outline: none;
position: relative;
display: block;
font-family: monospace;
Expand All @@ -21,7 +20,7 @@
-moz-box-sizing: border-box;
}

#autosuggest__input.autosuggest__input-open {
#autosuggest__input.autosuggest__input--open {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
Expand Down Expand Up @@ -50,33 +49,41 @@
margin: 0;
}

.autosuggest__results .autosuggest__results_item {
.autosuggest__results .autosuggest__results-item {
cursor: pointer;
padding: 15px;
}

#autosuggest ul:nth-child(1) > .autosuggest__results_title {
#autosuggest ul:nth-child(1) > .autosuggest__results-before {
border-top: none;
}

.autosuggest__results .autosuggest__results_title {
.autosuggest__results .autosuggest__results-before {
color: gray;
font-size: 11px;
margin-left: 0;
padding: 15px 13px 5px;
border-top: 1px solid lightgray;
}

.autosuggest__results .autosuggest__results_item:active,
.autosuggest__results .autosuggest__results_item:hover,
.autosuggest__results .autosuggest__results_item:focus,
.autosuggest__results .autosuggest__results-item:active,
.autosuggest__results .autosuggest__results-item:hover,
.autosuggest__results .autosuggest__results-item:focus,
.autosuggest__results
.autosuggest__results_item.autosuggest__results_item-highlighted {
background-color: #ddd;
.autosuggest__results-item.autosuggest__results-item--highlighted {
background-color: rgba(0,0,0,0.04);
}

.autosuggest__results{
overflow: scroll;
max-height: 400px;
}
</style>
}

.autosuggest__results .autosuggest__results-after {
color: gray;
font-size: 11px;
margin-left: 0;
padding: 15px 13px 5px;
border-top: 1px solid lightgray;
}
</style>
167 changes: 87 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ Place the component into your app!
```html
<vue-autosuggest
:suggestions="[{data:['Frodo', 'Samwise', 'Gandalf', 'Galadriel', 'Faramir', 'Éowyn']}]"
:input-props="{id:'autosuggest__input', onInputChange: onInputChange, placeholder:'Do you feel lucky, punk?'}"
:input-props="{id:'autosuggest__input', placeholder:'Do you feel lucky, punk?'}"
@input="onInputChange"
@selected="selectHandler"
@click="clickHandler"
>
Expand All @@ -117,18 +118,27 @@ Advanced usage:

```html
<template>
<div>
<h1>Vue-autosuggest 🔮</h1>
<div style="padding-top:10px; margin-bottom: 10px;"><span v-if="selected">You have selected '{{JSON.stringify(selected,null,2)}}'</span></div>
<vue-autosuggest
:suggestions="filteredOptions"
@focus="focusMe"
@click="clickHandler"
@selected="onSelected"
:render-suggestion="renderSuggestion"
:get-suggestion-value="getSuggestionValue"
:input-props="{id:'autosuggest__input', onInputChange: this.onInputChange, placeholder:'Do you feel lucky, punk?'}"/>
</div>
<div class="demo">
<div v-if="selected" style="padding-top:10px; width: 100%;">
You have selected <code>{{selected.name}}, the {{selected.race}}</code>
</div>
<div class="autosuggest-container">
<vue-autosuggest
v-model="query"
:suggestions="filteredOptions"
@focus="focusMe"
@click="clickHandler"
@input="onInputChange"
@selected="onSelected"
:get-suggestion-value="getSuggestionValue"
:input-props="{id:'autosuggest__input', placeholder:'Do you feel lucky, punk?'}">
<div slot-scope="{suggestion}" style="display: flex; align-items: center;">
<img :style="{ display: 'flex', width: '25px', height: '25px', borderRadius: '15px', marginRight: '10px'}" :src="suggestion.item.avatar" />
<div style="{ display: 'flex', color: 'navyblue'}">{{suggestion.item.name}}</div>
</div>
</vue-autosuggest>
</div>
</div>
</template>

<script>
Expand All @@ -140,71 +150,41 @@ export default {
},
data() {
return {
query: "",
selected: "",
filteredOptions: [],
suggestions: [
{
data: [
{ id: 1, name: "Frodo", avatar: "./frodo.jpg" },
{ id: 2, name: "Samwise", avatar: "./samwise.jpg" },
{ id: 3, name: "Gandalf", avatar: "./gandalf.png" },
{ id: 4, name: "Aragorn", avatar: "./aragorn.jpg" }
{ id: 1, name: "Frodo", race: "Hobbit", avatar: "https://upload.wikimedia.org/wikipedia/en/thumb/4/4e/Elijah_Wood_as_Frodo_Baggins.png/220px-Elijah_Wood_as_Frodo_Baggins.png" },
{ id: 2, name: "Samwise", race: "Hobbit", avatar: "https://upload.wikimedia.org/wikipedia/en/thumb/7/7b/Sean_Astin_as_Samwise_Gamgee.png/200px-Sean_Astin_as_Samwise_Gamgee.png" },
{ id: 3, name: "Gandalf", race: "Maia", avatar: "https://upload.wikimedia.org/wikipedia/en/thumb/e/e9/Gandalf600ppx.jpg/220px-Gandalf600ppx.jpg" },
{ id: 4, name: "Aragorn", race: "Human", avatar: "https://upload.wikimedia.org/wikipedia/en/thumb/3/35/Aragorn300ppx.png/150px-Aragorn300ppx.png" }
]
}
]
};
},
computed: {
filteredOptions() {
return [
{
data: this.suggestions[0].data.filter(option => {
return option.name.toLowerCase().indexOf(this.query.toLowerCase()) > -1;
})
}
];
}
},
methods: {
onInputChange(text, oldText) {
if (text === null) {
/* Maybe the text is null but you wanna do
* something else, but don't filter by null.
*/
return;
}
// Full customizability over filtering
const filteredData = this.suggestions[0].data.filter(option => {
return option.name.toLowerCase().indexOf(text.toLowerCase()) > -1;
});
// Store data in one property, and filtered in another
this.filteredOptions = [{ data: filteredData }];
},
clickHandler(item) {
// items are selected by default on click, but you can add some more behavior here!
// event fired when clicking on the input
},
onSelected(item) {
this.selected = item;
this.selected = item.item;
},
/**
* renderSuggestion will override the default suggestion template slot.
*/
renderSuggestion(suggestion) {
/* You will need babel-plugin-transform-vue-jsx for this kind of syntax for
* rendering. If you don't use babel or the jsx transform, then you can create
* the you can create the virtual node yourself using this.$createElement.
*/
const character = suggestion.item;
return (
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<img
style={{
width: "25px",
height: "25px",
borderRadius: "15px",
marginRight: "10px"
}}
src={character.avatar}
/>{" "}
<span style={{ color: "navyblue" }}>{character.name}</span>
</div>
);
onInputChange(text) {
// event fired when the input changes
console.log(text)
},
/**
* This is what the <input/> value is set to when you are selecting a suggestion.
Expand All @@ -213,11 +193,51 @@ export default {
return suggestion.item.name;
},
focusMe(e) {
console.log(e)
console.log(e) // FocusEvent
}
}
};
}
</script>

<style>
.demo {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
input {
width: 260px;
padding: 0.5rem;
}
ul {
width: 100%;
color: rgba(30, 39, 46,1.0);
list-style: none;
margin: 0;
padding: 0.5rem 0 .5rem 0;
}
li {
margin: 0 0 0 0;
border-radius: 5px;
padding: 0.75rem 0 0.75rem 0.75rem;
display: flex;
align-items: center;
}
li:hover {
cursor: pointer;
}
.autosuggest-container {
display: flex;
justify-content: center;
width: 280px;
}
#autosuggest { width: 100%; display: block;}
.autosuggest__results-item--highlighted {
background-color: rgba(51, 217, 178,0.2);
}
</style>
```

</p></details>
Expand Down Expand Up @@ -272,7 +292,7 @@ vue-autosuggest does not have an opinion about how you render the items in your
| [`section-configs`](#sectionConfigsProp) | Object | | Define multiple sections `<input>`. |
| [`render-suggestion`](#renderSuggestion) | Function | | Tell vue-autosuggest how to render inside the `<li>` tag. Overrides what is inside the default suggestion template slot. |
| [`get-suggestion-value`](#getSuggestionValue) | Function | | Tells vue-autosuggest what to put in the `<input/>` value |
| `@selected` | Function || suggestion select handler. equivalent to sectionConfigs `on-selected` but for all items |
| `@selected` | Function || suggestion select handler. equivalent to sectionConfigs `on-selected` but for all items |
| `component-attr-id-autosuggest` | String | | `id` of entire component |
| `component-attr-class-autosuggest-results-container` | String | | `class` of container of results container |
| `component-attr-class-autosuggest-results` | String | | `class` of results container |
Expand All @@ -284,10 +304,6 @@ vue-autosuggest does not have an opinion about how you render the items in your
| Prop | Type | Required | Description |
| :----------------------- | :------------------ | :--------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`id`](#inputPropsTable) | String || id attribute on `<input>`. |
| [`on-input-change`](#) | Function || Triggers everytime the `<input>` changes. This is triggered via a Vue watcher, so you have both current value, and previous value access e.g. `onInputChange(text, oldText)` |
| ~~onClick~~ | ~~Function~~ | Deprecated | ~~Triggers everytime the &lt;input> is clicked.~~ You can now use `@click` which will map to the underlying `<input />` |
| ~~onBlur~~ | ~~Function~~</span> | Deprecated | ~~HTML onblur event on &lt;input> same as Vue @blur event binding~~. You can now use `@blur` which will map to the underlying `<input />` |
| ~~onFocus~~ | ~~Function~~ | Deprecated | ~~HTML onfocus event on &lt;input> same as Vue @focus event binding~~ You can now use `@focus` which will map to the underlying `<input />` |
| [`initial-value`](#) | String | | Set some initial value for the `<input>`. |
| Any DOM Props | \* | | You can add any props to `<input>` as the component will `v-bind` inputProps. Similar to rest spread in JSX. See more details here: https://vuejs.org/v2/api/#v-bind. The `name` attribute is set to "`q`" by default. |

Expand Down Expand Up @@ -369,15 +385,6 @@ getSuggestionValue(suggestion) {
},
```

## FAQ

> How do I update the input programatically?
* You can assign a ref to the component `<vue-autosuggest ref="myRefName" ... />` and then access
the input value through `this.$refs.myRefName.searchInput`. This is useful mainly for clearing the
input. ⚠️ Note, refs are more of an "escape hatch" as they call it, so it won't trigger the
`onInputChange` method.

## Inspiration

* Misha Moroshko's react-autosuggest component inspired the api + WAI-ARIA completeness
Expand Down
Loading

0 comments on commit 458f3e2

Please sign in to comment.