Skip to content

Commit efc5093

Browse files
authored
Add API for overwriting default components (sagalbot#850)
* implement API for overwriting child components * add test coverage * update documentation for Components & Styling * update docs * refactor API, update docs * remove the service worker * fix tests
1 parent d522aca commit efc5093

15 files changed

+369
-19
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<div>
3+
<v-select
4+
:options="['Canada', 'United States']"
5+
:components="{Deselect}"
6+
/>
7+
</div>
8+
</template>
9+
10+
<script>
11+
import Vue from 'vue';
12+
13+
export default {
14+
computed: {
15+
Deselect () {
16+
return Vue.component('Deselect', {
17+
render (createElement) {
18+
return createElement('button', 'Clear');
19+
},
20+
});
21+
},
22+
},
23+
};
24+
</script>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<template>
2+
<div>
3+
<v-select
4+
class="style-chooser"
5+
placeholder="Choose a Styling Option"
6+
:options="['Components', 'CSS / Variables', 'Slots']"
7+
/>
8+
</div>
9+
</template>
10+
11+
<style>
12+
.style-chooser .vs__search::placeholder,
13+
.style-chooser .vs__dropdown-toggle,
14+
.style-chooser .vs__dropdown-menu {
15+
background: #dfe5fb;
16+
border: none;
17+
color: #394066;
18+
text-transform: lowercase;
19+
font-variant: small-caps;
20+
}
21+
22+
.style-chooser .vs__clear,
23+
.style-chooser .vs__open-indicator {
24+
fill: #394066;
25+
}
26+
</style>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<custom-select :options="['Vue.js', 'React', 'Angular']" />
3+
</template>
4+
5+
<script>
6+
import Vue from 'vue';
7+
import vSelect from '../../../src/components/Select';
8+
9+
const components = {
10+
Deselect: Vue.component('Deselect', {
11+
render: (createElement) => createElement('button', ''),
12+
}),
13+
OpenIndicator: Vue.component('OpenIndicator', {
14+
render: (createElement) => createElement('span', '🔽'),
15+
}),
16+
};
17+
18+
const mySelect = {...vSelect};
19+
20+
mySelect.props.components.default = () => components;
21+
22+
export default {
23+
components: {'custom-select': mySelect}
24+
}
25+
</script>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<template>
2+
<div>
3+
<v-select
4+
multiple
5+
v-model="selected"
6+
:options="['Canada', 'United States']"
7+
:components="{Deselect}"
8+
/>
9+
</div>
10+
</template>
11+
12+
<script>
13+
import Vue from 'vue';
14+
15+
export default {
16+
data: () => ({
17+
selected: ['Canada']
18+
}),
19+
computed: {
20+
Deselect () {
21+
return Vue.component('Deselect', {
22+
render (createElement) {
23+
return createElement('button', 'Clear');
24+
},
25+
});
26+
},
27+
},
28+
};
29+
</script>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<template>
2+
<div>
3+
<v-select
4+
multiple
5+
v-model="selected"
6+
:options="['Canada', 'United States']"
7+
:components="{OpenIndicator}"
8+
/>
9+
</div>
10+
</template>
11+
12+
<script>
13+
import Vue from 'vue';
14+
15+
export default {
16+
data: () => ({
17+
selected: ['Canada']
18+
}),
19+
computed: {
20+
OpenIndicator () {
21+
return Vue.component('OpenIndicator', {
22+
render (createElement) {
23+
return createElement('i', '🤘🏻');
24+
},
25+
});
26+
},
27+
},
28+
};
29+
</script>

docs/.vuepress/config.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,8 @@ module.exports = {
6868
ga: isDeployPreview ? '' : 'UA-12818324-8',
6969
},
7070
'@vuepress/pwa': {
71-
serviceWorker: true,
72-
updatePopup: {
73-
message: 'New content is available.',
74-
buttonText: 'Refresh',
75-
},
71+
serviceWorker: false,
72+
updatePopup: true,
7673
},
7774
'@vuepress/plugin-register-components': {},
7875
'@vuepress/plugin-active-header-links': {},
@@ -100,11 +97,19 @@ module.exports = {
10097
['guide/upgrading', 'Upgrading 2.x to 3.x'],
10198
],
10299
},
100+
{
101+
title: 'Templating & Styling',
102+
collapsable: false,
103+
children: [
104+
['guide/components', 'Child Components'],
105+
['guide/css', 'CSS & Selectors'],
106+
['guide/slots', 'Slots'],
107+
],
108+
},
103109
{
104110
title: 'Digging Deeper',
105111
collapsable: false,
106112
children: [
107-
['guide/templating', 'Templating'],
108113
['guide/vuex', 'Vuex'],
109114
['guide/ajax', 'AJAX'],
110115
],

docs/api/slots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
::: tip
2-
VueSelect leverages scoped slots to allow for total customization of the presentation layer.
2+
Vue Select leverages scoped slots to allow for total customization of the presentation layer.
33
Slots can be used to change the look and feel of the UI, or to simply swap out text.
44
:::
55

docs/guide/components.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
Vue Select utilizes child components throughout, and exposes an API to overwrite these components
2+
with your own, using the `components` `{Object}` prop. When implementing the `components` prop in
3+
your code, Vue Select merge it's default components with any keys that you set in the object.
4+
5+
Your object will be merged with the object that is exported below:
6+
7+
<<< @/src/components/childComponents.js{4-7}
8+
9+
You can override the value of any of these keys with your own components.
10+
11+
## Available Components
12+
13+
### Deselect
14+
15+
You may wish to roll your own deselect button. `Deselect` is used within tags on
16+
`multiple` selects, and serves as the clear button for single selects. Maybe you just want to use
17+
a simple `<button>Clear</button>` instead.
18+
19+
```html
20+
<v-select :components="{Deselect}" />
21+
```
22+
23+
```js
24+
computed: {
25+
Deselect() {
26+
return Vue.component('Deselect', {
27+
render (createElement) {
28+
return createElement('button', 'Clear')
29+
}
30+
})
31+
}
32+
}
33+
```
34+
35+
<ClearButtonOverride />
36+
37+
The same approach applies for `multiple` selects:
38+
39+
<MultipleClearButtonOverride />
40+
41+
### OpenIndicator
42+
43+
The `OpenIndicator` component is the 'caret' used within the component that adjusts orientation
44+
based on whether the dropdown is open or closed.
45+
46+
```html
47+
<v-select :components="{OpenIndicator}" />
48+
```
49+
```js
50+
computed: {
51+
OpenIndicator () {
52+
return Vue.component('OpenIndicator', {
53+
render (createElement) {
54+
return createElement('button', '🤘🏻');
55+
},
56+
});
57+
},
58+
},
59+
```
60+
61+
<OpenIndicatorOverride />
62+
63+
## Setting at Registration
64+
65+
If you want to all instances of Vue Select to use your custom components throughout your app, while
66+
only having to set the implementation once, you can do so when registering Vue Select as a component.
67+
68+
```js
69+
import Vue from 'vue';
70+
import vSelect from 'vue-select';
71+
72+
/**
73+
* Create custom components to override defaults.
74+
* @type {{OpenIndicator: *, Deselect: *}}
75+
*/
76+
const components = {
77+
Deselect: Vue.component('Deselect', {
78+
render: (createElement) => createElement('button', ''),
79+
}),
80+
OpenIndicator: Vue.component('OpenIndicator', {
81+
render: (createElement) => createElement('span', '🔽'),
82+
}),
83+
};
84+
85+
// Set the components prop default to return our fresh components
86+
vSelect.props.components.default = () => components;
87+
88+
// Register the component
89+
Vue.component(vSelect)
90+
```
91+
92+
<CustomComponentRegistration />
93+
94+

docs/guide/css.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
Vue Select offers many APIs for customizing the look and feel from the component. You can use
2+
[scoped slots](../api/slots.md), [custom child components](components.md), or modify the built in
3+
SCSS variables.
4+
5+
::: tip
6+
Support for CSS variables (custom properties) is currently on the road map for those
7+
that are not using sass in their projects.
8+
:::
9+
10+
## SCSS Variables
11+
12+
Variables are leveraged in as much of the component styles as possible. If you really want to dig
13+
into the SCSS, the files are located in `src/scss`. The variables listed below can be found at
14+
[`src/scss/global/_variables`](https://github.com/sagalbot/vue-select/blob/master/src/scss/global/_variables.scss).
15+
16+
All variables are implemented with `!default` in order to make them easier to override in your
17+
application.
18+
19+
<<< @/src/scss/global/_variables.scss
20+
21+
## Overriding Default Styles
22+
23+
Vue Select takes the approach of using selectors with a single level of specificity, while using
24+
classes that are very specific to Vue Select to avoid collisions with your app.
25+
26+
All classes within Vue Select use the `vs__` prefix, and selectors are generally a single classname
27+
– unless there is a state being applied to the component.
28+
29+
In order to override a default property in your app, you should add one level of specificity.
30+
The easiest way to do this, is to add `.v-select` before the `vs__*` selector if you want to adjust
31+
all instances of Vue Select, or add your own classname if you just want to affect one.
32+
33+
<CssSpecificity />
34+
35+
<<< @/docs/.vuepress/components/CssSpecificity.vue
36+
37+

docs/guide/templating.md renamed to docs/guide/slots.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
::: tip 🚧
2+
This section of the guide is a work in progress! Check back soon for an update.
3+
Vue Select currently offers quite a few scoped slots, and you can check out the
4+
[API Docs for Slots](../api/slots.md) in the meantime while a good guide is put together.
5+
:::
6+
17
#### Scoped Slot `option`
28

39
vue-select provides the scoped `option` slot in order to create custom dropdown templates.

0 commit comments

Comments
 (0)