forked from chatwoot/chatwoot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Creates new component for multiselect. (chatwoot#2446)
- Loading branch information
Showing
7 changed files
with
500 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,3 +34,4 @@ exclude_patterns: | |
- "stories/**/*" | ||
- "**/*.stories.js" | ||
- "**/stories/" | ||
- "app/javascript/**/*.stories.js" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,7 +90,7 @@ | |
} | ||
}, | ||
"SEARCH": { | ||
"NO_RESULTS": "No agents found." | ||
"NO_RESULTS": "No results found." | ||
} | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
app/javascript/shared/components/ui/MultiselectDropdown.stories.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { action } from '@storybook/addon-actions'; | ||
import Dropdown from './MutiselectDropdown'; | ||
|
||
export default { | ||
title: 'Components/Dropdown/Multiselect Dropdown', | ||
component: Dropdown, | ||
argTypes: { | ||
options: { | ||
control: { | ||
type: 'object', | ||
}, | ||
}, | ||
selectedItem: { | ||
control: { | ||
type: 'object', | ||
}, | ||
}, | ||
multiselectorTitle: { | ||
control: { | ||
type: 'text', | ||
}, | ||
}, | ||
multiselectorPlaceholder: { | ||
control: { | ||
type: 'text', | ||
}, | ||
}, | ||
noSearchResult: { | ||
control: { | ||
type: 'text', | ||
}, | ||
}, | ||
inputPlaceholder: { | ||
control: { | ||
type: 'text', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const Template = (args, { argTypes }) => ({ | ||
props: Object.keys(argTypes), | ||
components: { Dropdown }, | ||
template: '<dropdown v-bind="$props" @click="onClick"></dropdown>', | ||
}); | ||
|
||
export const MultiselectDropdown = Template.bind({}); | ||
MultiselectDropdown.args = { | ||
onClick: action('Opened'), | ||
options: [ | ||
{ | ||
id: 1, | ||
availability_status: 'online', | ||
name: 'James Philip', | ||
thumbnail: 'https://randomuser.me/api/portraits/men/4.jpg', | ||
}, | ||
], | ||
|
||
selectedItem: { | ||
id: 1, | ||
availability_status: 'online', | ||
name: 'James Philip', | ||
thumbnail: 'https://randomuser.me/api/portraits/men/4.jpg', | ||
}, | ||
}; |
67 changes: 67 additions & 0 deletions
67
app/javascript/shared/components/ui/MultiselectDropdownItems.stories.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { action } from '@storybook/addon-actions'; | ||
import DropdownItems from './MultiselectDropdownItems'; | ||
|
||
export default { | ||
title: 'Components/Dropdown/Multiselect Dropdown Items', | ||
component: DropdownItems, | ||
argTypes: { | ||
options: { | ||
control: { | ||
type: 'object', | ||
}, | ||
}, | ||
selectedItem: { | ||
control: { | ||
type: 'object', | ||
}, | ||
}, | ||
inputPlaceholder: { | ||
control: { | ||
type: 'text', | ||
}, | ||
}, | ||
noSearchResult: { | ||
control: { | ||
type: 'text', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const Template = (args, { argTypes }) => ({ | ||
props: Object.keys(argTypes), | ||
components: { DropdownItems }, | ||
template: | ||
'<dropdown-items v-bind="$props" @click="onClick"></dropdown-items>', | ||
}); | ||
|
||
export const MultiselectDropdownItems = Template.bind({}); | ||
MultiselectDropdownItems.args = { | ||
onClick: action('Added'), | ||
options: [ | ||
{ | ||
id: '0', | ||
name: 'None', | ||
thumbnail: '', | ||
}, | ||
{ | ||
id: '1', | ||
name: 'James Philip', | ||
availability_status: 'online', | ||
role: 'administrator', | ||
thumbnail: 'https://randomuser.me/api/portraits/men/4.jpg', | ||
}, | ||
{ | ||
id: '2', | ||
name: 'Support Team', | ||
thumbnail: '', | ||
}, | ||
{ | ||
id: '3', | ||
name: 'Agent', | ||
thumbnail: '', | ||
}, | ||
], | ||
|
||
selectedItem: { id: '1' }, | ||
}; |
197 changes: 197 additions & 0 deletions
197
app/javascript/shared/components/ui/MultiselectDropdownItems.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
<template> | ||
<div class="dropdown-wrap"> | ||
<div class="search-wrap"> | ||
<input | ||
ref="searchbar" | ||
v-model="search" | ||
type="text" | ||
class="search-input" | ||
autofocus="true" | ||
:placeholder="inputPlaceholder" | ||
/> | ||
</div> | ||
<div class="list-scroll-container"> | ||
<div class="dropdown-list"> | ||
<woot-dropdown-menu> | ||
<woot-dropdown-item | ||
v-for="option in filteredOptions" | ||
:key="option.id" | ||
> | ||
<woot-button | ||
class="dropdown-item" | ||
variant="clear" | ||
:class="{ | ||
active: option.id === (selectedItem && selectedItem.id), | ||
}" | ||
@click="() => onclick(option)" | ||
> | ||
<div class="user-wrap"> | ||
<Thumbnail | ||
:src="option.thumbnail" | ||
size="24px" | ||
:username="option.name" | ||
:status="option.availability_status" | ||
/> | ||
<div class="name-wrap"> | ||
<span | ||
class="name text-truncate text-block-title" | ||
:title="option.name" | ||
> | ||
{{ option.name }} | ||
</span> | ||
<i | ||
v-if="option.id === (selectedItem && selectedItem.id)" | ||
class="icon ion-checkmark-round" | ||
/> | ||
</div> | ||
</div> | ||
</woot-button> | ||
</woot-dropdown-item> | ||
</woot-dropdown-menu> | ||
<h4 v-if="noResult" class="no-result text-truncate text-block-title"> | ||
{{ noSearchResult }} | ||
</h4> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue'; | ||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue'; | ||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue'; | ||
export default { | ||
components: { | ||
WootDropdownItem, | ||
WootDropdownMenu, | ||
Thumbnail, | ||
}, | ||
props: { | ||
options: { | ||
type: Array, | ||
default: () => [], | ||
}, | ||
selectedItem: { | ||
type: Object, | ||
default: () => ({}), | ||
}, | ||
inputPlaceholder: { | ||
type: String, | ||
default: 'Search', | ||
}, | ||
noSearchResult: { | ||
type: String, | ||
default: 'No results found', | ||
}, | ||
}, | ||
data() { | ||
return { | ||
search: '', | ||
}; | ||
}, | ||
computed: { | ||
filteredOptions() { | ||
return this.options.filter(option => { | ||
return option.name.toLowerCase().includes(this.search.toLowerCase()); | ||
}); | ||
}, | ||
noResult() { | ||
return this.filteredOptions.length === 0 && this.search !== ''; | ||
}, | ||
}, | ||
mounted() { | ||
this.focusInput(); | ||
}, | ||
methods: { | ||
onclick(option) { | ||
this.$emit('click', option); | ||
}, | ||
focusInput() { | ||
this.$refs.searchbar.focus(); | ||
}, | ||
}, | ||
}; | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
.dropdown-wrap { | ||
width: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
max-height: 16rem; | ||
} | ||
.search-wrap { | ||
margin-bottom: var(--space-small); | ||
flex: 0 0 auto; | ||
max-height: var(--space-large); | ||
} | ||
.search-input { | ||
margin: 0; | ||
width: 100%; | ||
border: 1px solid transparent; | ||
height: var(--space-large); | ||
font-size: var(--font-size-small); | ||
padding: var(--space-small); | ||
background-color: var(--color-background); | ||
&:focus { | ||
border: 1px solid var(--w-500); | ||
} | ||
} | ||
.list-scroll-container { | ||
display: flex; | ||
justify-content: flex-start; | ||
align-items: flex-start; | ||
flex: 1 1 auto; | ||
overflow: auto; | ||
} | ||
.dropdown-list { | ||
width: 100%; | ||
max-height: 12rem; | ||
} | ||
.dropdown-item { | ||
justify-content: space-between; | ||
width: 100%; | ||
&.active { | ||
font-weight: var(--font-weight-bold); | ||
} | ||
} | ||
.user-wrap { | ||
display: flex; | ||
align-items: center; | ||
} | ||
.name-wrap { | ||
display: flex; | ||
justify-content: space-between; | ||
min-width: 0; | ||
width: 100%; | ||
} | ||
.name { | ||
line-height: var(--space-normal); | ||
margin: 0 var(--space-small); | ||
} | ||
.icon { | ||
margin-left: var(--space-smaller); | ||
} | ||
.no-result { | ||
display: flex; | ||
justify-content: center; | ||
width: 100%; | ||
padding: var(--space-small) var(--space-one); | ||
} | ||
</style> |
Oops, something went wrong.