feat: Vite + vue 3 💚 (#10047)
Fixes https://github.com/chatwoot/chatwoot/issues/8436 Fixes https://github.com/chatwoot/chatwoot/issues/9767 Fixes https://github.com/chatwoot/chatwoot/issues/10156 Fixes https://github.com/chatwoot/chatwoot/issues/6031 Fixes https://github.com/chatwoot/chatwoot/issues/5696 Fixes https://github.com/chatwoot/chatwoot/issues/9250 Fixes https://github.com/chatwoot/chatwoot/issues/9762 --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -69,7 +69,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.branding--image {
|
||||
margin-right: $space-smaller;
|
||||
|
||||
@@ -48,21 +48,11 @@ export default {
|
||||
return styles;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onClick(e) {
|
||||
this.$emit('click', e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:class="buttonClassName"
|
||||
:style="buttonStyles"
|
||||
:disabled="disabled"
|
||||
@click="onClick"
|
||||
>
|
||||
<button :class="buttonClassName" :style="buttonStyles" :disabled="disabled">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
@@ -56,8 +56,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~dashboard/assets/scss/mixins.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.action-button {
|
||||
align-items: center;
|
||||
|
||||
@@ -56,8 +56,8 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~dashboard/assets/scss/mixins.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
@import 'dashboard/assets/scss/mixins.scss';
|
||||
|
||||
.card-message {
|
||||
max-width: 220px;
|
||||
|
||||
@@ -167,7 +167,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.form {
|
||||
padding: $space-normal;
|
||||
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click', this.action);
|
||||
this.$emit('optionSelect', this.action);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.option {
|
||||
border-radius: $space-jumbo;
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
return this.selected === option.id;
|
||||
},
|
||||
onClick(selectedOption) {
|
||||
this.$emit('click', selectedOption);
|
||||
this.$emit('optionSelect', selectedOption);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
:key="option.id"
|
||||
:action="option"
|
||||
:is-selected="isSelected(option)"
|
||||
@click="onClick"
|
||||
@option-select="onClick"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -68,7 +68,8 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~dashboard/assets/scss/variables.scss';
|
||||
@import 'dashboard/assets/scss/variables.scss';
|
||||
|
||||
.has-selected {
|
||||
.option-button:not(.is-selected) {
|
||||
color: $color-light-gray;
|
||||
@@ -78,7 +79,7 @@ export default {
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.options-message {
|
||||
max-width: 17rem;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import CustomerSatisfaction from './CustomerSatisfaction';
|
||||
|
||||
export default {
|
||||
title: 'Components/CustomerSatisfaction',
|
||||
component: CustomerSatisfaction,
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { CustomerSatisfaction },
|
||||
template: '<customer-satisfaction />',
|
||||
});
|
||||
|
||||
export const Item = Template.bind({});
|
||||
Item.args = {
|
||||
onClick: action('Selected'),
|
||||
};
|
||||
@@ -156,8 +156,8 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~widget/assets/scss/mixins.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/mixins.scss';
|
||||
|
||||
.customer-satisfaction {
|
||||
@include light-shadow;
|
||||
@@ -204,6 +204,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.feedback-form {
|
||||
display: flex;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~widget/assets/scss/variables';
|
||||
@import 'widget/assets/scss/variables';
|
||||
|
||||
.date--separator {
|
||||
font-size: $font-size-default;
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
@@ -82,7 +82,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
if (this.value) {
|
||||
if (this.modelValue) {
|
||||
this.resizeTextarea();
|
||||
this.setCursor();
|
||||
} else {
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
methods: {
|
||||
resizeTextarea() {
|
||||
this.$el.style.height = 'auto';
|
||||
if (!this.value) {
|
||||
if (!this.modelValue) {
|
||||
this.$el.style.height = `${this.minHeight}rem`;
|
||||
} else {
|
||||
this.$el.style.height = `${this.$el.scrollHeight}px`;
|
||||
@@ -104,9 +104,10 @@ export default {
|
||||
// is supposed to be added, else we remove it.
|
||||
toggleSignatureInEditor(signatureEnabled) {
|
||||
const valueWithSignature = signatureEnabled
|
||||
? appendSignature(this.value, this.cleanedSignature)
|
||||
: removeSignature(this.value, this.cleanedSignature);
|
||||
? appendSignature(this.modelValue, this.cleanedSignature)
|
||||
: removeSignature(this.modelValue, this.cleanedSignature);
|
||||
|
||||
this.$emit('update:modelValue', valueWithSignature);
|
||||
this.$emit('input', valueWithSignature);
|
||||
|
||||
this.$nextTick(() => {
|
||||
@@ -116,7 +117,7 @@ export default {
|
||||
},
|
||||
setCursor() {
|
||||
const bodyWithoutSignature = removeSignature(
|
||||
this.value,
|
||||
this.modelValue,
|
||||
this.cleanedSignature
|
||||
);
|
||||
|
||||
@@ -130,6 +131,7 @@ export default {
|
||||
}
|
||||
},
|
||||
onInput(event) {
|
||||
this.$emit('update:modelValue', event.target.value);
|
||||
this.$emit('input', event.target.value);
|
||||
this.resizeTextarea();
|
||||
},
|
||||
@@ -155,7 +157,7 @@ export default {
|
||||
ref="textarea"
|
||||
:placeholder="placeholder"
|
||||
:rows="rows"
|
||||
:value="value"
|
||||
:value="modelValue"
|
||||
@input="onInput"
|
||||
@focus="onFocus"
|
||||
@keyup="onKeyup"
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
@mixin color-spinner() {
|
||||
@keyframes spinner {
|
||||
|
||||
@@ -9,7 +9,7 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
@@ -18,9 +18,14 @@ export default {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(event) {
|
||||
this.$emit('input', event.target.value);
|
||||
computed: {
|
||||
computedModel: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -39,6 +44,7 @@ export default {
|
||||
{{ label }}
|
||||
</div>
|
||||
<textarea
|
||||
v-model="computedModel"
|
||||
class="w-full px-3 py-2 leading-tight border rounded outline-none resize-none text-slate-700"
|
||||
:class="{
|
||||
'border-black-200 hover:border-black-300 focus:border-black-300':
|
||||
@@ -46,8 +52,6 @@ export default {
|
||||
'border-red-200 hover:border-red-300 focus:border-red-300': error,
|
||||
}"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@change="onChange"
|
||||
/>
|
||||
<div v-if="error" class="mt-2 text-xs font-medium text-red-400">
|
||||
{{ error }}
|
||||
|
||||
77
app/javascript/shared/components/charts/BarChart.vue
Normal file
77
app/javascript/shared/components/charts/BarChart.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { Bar } from 'vue-chartjs';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
} from 'chart.js';
|
||||
|
||||
const props = defineProps({
|
||||
collection: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
chartOptions: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
ChartJS.register(Title, Tooltip, BarElement, CategoryScale, LinearScale);
|
||||
|
||||
const fontFamily =
|
||||
'Inter,-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
|
||||
|
||||
const defaultChartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
fontFamily,
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
datasets: {
|
||||
bar: {
|
||||
barPercentage: 1.0,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
fontFamily: fontFamily,
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
position: 'left',
|
||||
ticks: {
|
||||
fontFamily: fontFamily,
|
||||
beginAtZero: true,
|
||||
stepSize: 1,
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const options = computed(() => {
|
||||
return { ...defaultChartOptions, ...props.chartOptions };
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Bar :data="collection" :options="options" />
|
||||
</template>
|
||||
@@ -184,6 +184,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.box-shadow-blue {
|
||||
box-shadow:
|
||||
@@ -200,7 +201,7 @@ export default {
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~dashboard/assets/scss/mixins';
|
||||
@import 'dashboard/assets/scss/mixins';
|
||||
|
||||
.emoji-dialog {
|
||||
&::before {
|
||||
@@ -210,6 +211,7 @@ export default {
|
||||
$color-bg-dark: #26292b;
|
||||
@include arrow(bottom, $color-bg-dark, $space-slab);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
$color-bg: #ebf0f5;
|
||||
@include arrow(bottom, $color-bg, $space-slab);
|
||||
@@ -245,6 +247,7 @@ export default {
|
||||
.emoji-icon {
|
||||
@apply text-slate-200 dark:text-slate-200 mb-2;
|
||||
}
|
||||
|
||||
.empty-message--text {
|
||||
@apply text-slate-200 dark:text-slate-200 text-sm font-medium;
|
||||
}
|
||||
@@ -271,6 +274,7 @@ export default {
|
||||
li .active {
|
||||
@apply bg-white dark:bg-slate-900;
|
||||
}
|
||||
|
||||
.emoji--item {
|
||||
@apply items-center flex text-sm;
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import Dropdown from './MultiselectDropdown';
|
||||
|
||||
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',
|
||||
},
|
||||
};
|
||||
@@ -1,142 +1,130 @@
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, defineEmits } from 'vue';
|
||||
import { OnClickOutside } from '@vueuse/components';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import MultiselectDropdownItems from 'shared/components/ui/MultiselectDropdownItems.vue';
|
||||
export default {
|
||||
components: {
|
||||
Thumbnail,
|
||||
MultiselectDropdownItems,
|
||||
},
|
||||
props: {
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedItem: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
hasThumbnail: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
multiselectorTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multiselectorPlaceholder: {
|
||||
type: String,
|
||||
default: 'None',
|
||||
},
|
||||
noSearchResult: {
|
||||
type: String,
|
||||
default: 'No results found',
|
||||
},
|
||||
inputPlaceholder: {
|
||||
type: String,
|
||||
default: 'Search',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSearchDropdown: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasValue() {
|
||||
if (this.selectedItem && this.selectedItem.id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleDropdown() {
|
||||
this.showSearchDropdown = !this.showSearchDropdown;
|
||||
},
|
||||
|
||||
onCloseDropdown() {
|
||||
this.showSearchDropdown = false;
|
||||
},
|
||||
|
||||
onClickSelectItem(value) {
|
||||
this.$emit('click', value);
|
||||
this.onCloseDropdown();
|
||||
},
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedItem: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
hasThumbnail: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
multiselectorTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multiselectorPlaceholder: {
|
||||
type: String,
|
||||
default: 'None',
|
||||
},
|
||||
noSearchResult: {
|
||||
type: String,
|
||||
default: 'No results found',
|
||||
},
|
||||
inputPlaceholder: {
|
||||
type: String,
|
||||
default: 'Search',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['select']);
|
||||
const [showSearchDropdown, toggleDropdown] = useToggle(false);
|
||||
|
||||
const onCloseDropdown = () => toggleDropdown(false);
|
||||
const onClickSelectItem = value => {
|
||||
emit('select', value);
|
||||
onCloseDropdown();
|
||||
};
|
||||
|
||||
const hasValue = computed(() => {
|
||||
if (props.selectedItem && props.selectedItem.id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-on-clickaway="onCloseDropdown"
|
||||
class="relative w-full mb-2"
|
||||
@keyup.esc="onCloseDropdown"
|
||||
>
|
||||
<woot-button
|
||||
variant="hollow"
|
||||
color-scheme="secondary"
|
||||
class="w-full border border-solid border-slate-100 dark:border-slate-700 px-2 hover:border-slate-75 dark:hover:border-slate-600"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<div class="flex gap-1">
|
||||
<Thumbnail
|
||||
v-if="hasValue && hasThumbnail"
|
||||
:src="selectedItem.thumbnail"
|
||||
size="24px"
|
||||
:status="selectedItem.availability_status"
|
||||
:username="selectedItem.name"
|
||||
/>
|
||||
<div class="flex justify-between w-full min-w-0 items-center">
|
||||
<h4
|
||||
v-if="!hasValue"
|
||||
class="text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
>
|
||||
{{ multiselectorPlaceholder }}
|
||||
</h4>
|
||||
<h4
|
||||
v-else
|
||||
class="items-center leading-tight overflow-hidden whitespace-nowrap text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
:title="selectedItem.name"
|
||||
>
|
||||
{{ selectedItem.name }}
|
||||
</h4>
|
||||
<i
|
||||
v-if="showSearchDropdown"
|
||||
class="icon ion-chevron-up text-slate-600 mr-1"
|
||||
<OnClickOutside @trigger="onCloseDropdown">
|
||||
<div class="relative w-full mb-2" @keyup.esc="onCloseDropdown">
|
||||
<woot-button
|
||||
variant="hollow"
|
||||
color-scheme="secondary"
|
||||
class="w-full border border-solid border-slate-100 dark:border-slate-700 px-2 hover:border-slate-75 dark:hover:border-slate-600"
|
||||
@click="
|
||||
() => toggleDropdown() // ensure that the event is not passed to the button
|
||||
"
|
||||
>
|
||||
<div class="flex gap-1">
|
||||
<Thumbnail
|
||||
v-if="hasValue && hasThumbnail"
|
||||
:src="selectedItem.thumbnail"
|
||||
size="24px"
|
||||
:status="selectedItem.availability_status"
|
||||
:username="selectedItem.name"
|
||||
/>
|
||||
<i v-else class="icon ion-chevron-down text-slate-600 mr-1" />
|
||||
<div class="flex justify-between w-full min-w-0 items-center">
|
||||
<h4
|
||||
v-if="!hasValue"
|
||||
class="text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
>
|
||||
{{ multiselectorPlaceholder }}
|
||||
</h4>
|
||||
<h4
|
||||
v-else
|
||||
class="items-center leading-tight overflow-hidden whitespace-nowrap text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
:title="selectedItem.name"
|
||||
>
|
||||
{{ selectedItem.name }}
|
||||
</h4>
|
||||
<i
|
||||
v-if="showSearchDropdown"
|
||||
class="icon i-lucide-chevron-up text-slate-600 mr-1"
|
||||
/>
|
||||
<i v-else class="icon i-lucide-chevron-down text-slate-600 mr-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</woot-button>
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdown }"
|
||||
class="dropdown-pane"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<h4
|
||||
class="text-sm text-slate-800 dark:text-slate-100 m-0 overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ multiselectorTitle }}
|
||||
</h4>
|
||||
<woot-button
|
||||
icon="dismiss"
|
||||
size="tiny"
|
||||
color-scheme="secondary"
|
||||
variant="clear"
|
||||
@click="onCloseDropdown"
|
||||
</woot-button>
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdown }"
|
||||
class="dropdown-pane"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<h4
|
||||
class="text-sm text-slate-800 dark:text-slate-100 m-0 overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ multiselectorTitle }}
|
||||
</h4>
|
||||
<woot-button
|
||||
icon="dismiss"
|
||||
size="tiny"
|
||||
color-scheme="secondary"
|
||||
variant="clear"
|
||||
@click="onCloseDropdown"
|
||||
/>
|
||||
</div>
|
||||
<MultiselectDropdownItems
|
||||
v-if="showSearchDropdown"
|
||||
:options="options"
|
||||
:selected-items="[selectedItem]"
|
||||
:has-thumbnail="hasThumbnail"
|
||||
:input-placeholder="inputPlaceholder"
|
||||
:no-search-result="noSearchResult"
|
||||
@select="onClickSelectItem"
|
||||
/>
|
||||
</div>
|
||||
<MultiselectDropdownItems
|
||||
v-if="showSearchDropdown"
|
||||
:options="options"
|
||||
:selected-items="[selectedItem]"
|
||||
:has-thumbnail="hasThumbnail"
|
||||
:input-placeholder="inputPlaceholder"
|
||||
:no-search-result="noSearchResult"
|
||||
@click="onClickSelectItem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</OnClickOutside>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
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',
|
||||
},
|
||||
},
|
||||
selectedItems: {
|
||||
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: '',
|
||||
},
|
||||
],
|
||||
|
||||
selectedItems: [{ id: '1' }],
|
||||
};
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
onclick(option) {
|
||||
this.$emit('click', option);
|
||||
this.$emit('select', option);
|
||||
},
|
||||
focusInput() {
|
||||
this.$refs.searchbar.focus();
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import AddLabel from './AddLabel';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label/Add Button',
|
||||
component: AddLabel,
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { AddLabel },
|
||||
template: '<add-label v-bind="$props" @add="onClick"></add-label>',
|
||||
});
|
||||
|
||||
export const AddButton = Template.bind({});
|
||||
AddButton.args = {
|
||||
onClick: action('opened'),
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import LabelDropdown from './LabelDropdown';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label/Dropdown',
|
||||
component: LabelDropdown,
|
||||
argTypes: {
|
||||
conversationId: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
accountLabels: {
|
||||
defaultValue: [
|
||||
{
|
||||
color: '#555',
|
||||
description: '',
|
||||
id: 1,
|
||||
title: 'sales',
|
||||
},
|
||||
{
|
||||
color: '#c242f5',
|
||||
description: '',
|
||||
id: 1,
|
||||
title: 'business',
|
||||
},
|
||||
{
|
||||
color: '#4287f5',
|
||||
description: '',
|
||||
id: 1,
|
||||
title: 'testing',
|
||||
},
|
||||
],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
selectedLabels: {
|
||||
defaultValue: 'sales, testing',
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { LabelDropdown },
|
||||
template:
|
||||
'<label-dropdown v-bind="$props" @add="onAdd" @remove="onRemove"></label-dropdown>',
|
||||
});
|
||||
|
||||
export const Dropdown = Template.bind({});
|
||||
Dropdown.args = {
|
||||
onAdd: action('added'),
|
||||
onRemove: action('removed'),
|
||||
};
|
||||
@@ -142,7 +142,7 @@ export default {
|
||||
:title="label.title"
|
||||
:color="label.color"
|
||||
:selected="selectedLabels.includes(label.title)"
|
||||
@click="onAddRemove(label)"
|
||||
@select-label="onAddRemove(label)"
|
||||
/>
|
||||
</woot-dropdown-menu>
|
||||
<div
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import LabelDropdownItem from './LabelDropdownItem';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label/Item',
|
||||
component: LabelDropdownItem,
|
||||
argTypes: {
|
||||
title: {
|
||||
defaultValue: 'sales',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
color: {
|
||||
defaultValue: '#555',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
selected: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { LabelDropdownItem },
|
||||
template:
|
||||
'<label-dropdown-item v-bind="$props" @click="onClick"></label-dropdown-item>',
|
||||
});
|
||||
|
||||
export const Item = Template.bind({});
|
||||
Item.args = {
|
||||
onClick: action('Selected'),
|
||||
};
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click', this.title);
|
||||
this.$emit('selectLabel', this.title);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
<span class="label-text" :title="title">{{ title }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<i v-if="selected" class="icon ion-checkmark-round" />
|
||||
<i v-if="selected" class="i-lucide-circle-check" />
|
||||
</div>
|
||||
</div>
|
||||
</woot-button>
|
||||
|
||||
Reference in New Issue
Block a user