198 lines
5.1 KiB
Vue
198 lines
5.1 KiB
Vue
<template>
|
|
<NestedPopover v-if="options">
|
|
<template #target>
|
|
<Button label="Sort" ref="sortButtonRef">
|
|
<template #prefix><SortIcon class="h-4" /></template>
|
|
<template v-if="sortValues.length" #suffix>
|
|
<div
|
|
class="flex justify-center items-center w-5 h-5 text-2xs font-medium pt-[1px] bg-gray-900 text-white rounded"
|
|
>
|
|
{{ sortValues.length }}
|
|
</div>
|
|
</template>
|
|
</Button>
|
|
</template>
|
|
<template #body="{ close }">
|
|
<div class="rounded-lg border border-gray-100 bg-white shadow-xl my-2">
|
|
<div class="p-2 min-w-[352px]">
|
|
<div
|
|
v-if="sortValues.length"
|
|
id="sort-list"
|
|
class="flex flex-col gap-2 mb-3"
|
|
>
|
|
<div
|
|
v-for="(sort, i) in sortValues"
|
|
:key="sort.fieldname"
|
|
class="flex items-center gap-2"
|
|
>
|
|
<div class="flex items-center justify-center h-7 w-7 handle">
|
|
<DragIcon class="h-4 w-4 cursor-grab text-gray-600" />
|
|
</div>
|
|
<Autocomplete
|
|
class="!w-32"
|
|
:value="sort.fieldname"
|
|
:options="sortOptions.data"
|
|
@change="(e) => updateSort(e, i)"
|
|
placeholder="Sort by"
|
|
/>
|
|
<FormControl
|
|
class="!w-32"
|
|
type="select"
|
|
v-model="sort.direction"
|
|
:options="[
|
|
{ label: 'Ascending', value: 'asc' },
|
|
{ label: 'Descending', value: 'desc' },
|
|
]"
|
|
placeholder="Sort by"
|
|
/>
|
|
<Button variant="ghost" icon="x" @click="removeSort(i)" />
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="text-gray-600 flex items-center text-sm px-3 h-7 mb-3"
|
|
>
|
|
Empty - Choose a field to sort by
|
|
</div>
|
|
<div class="flex items-center justify-between gap-2">
|
|
<Autocomplete
|
|
:options="options"
|
|
value=""
|
|
placeholder="Sort by"
|
|
@change="(e) => setSort(e)"
|
|
>
|
|
<template #target="{ togglePopover }">
|
|
<Button
|
|
class="!text-gray-600"
|
|
variant="ghost"
|
|
@click="togglePopover()"
|
|
label="Add sort"
|
|
>
|
|
<template #prefix>
|
|
<FeatherIcon name="plus" class="h-4" />
|
|
</template>
|
|
</Button>
|
|
</template>
|
|
</Autocomplete>
|
|
<Button
|
|
v-if="sortValues.length"
|
|
class="!text-gray-600"
|
|
variant="ghost"
|
|
label="Clear sort"
|
|
@click="clearSort(close)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</NestedPopover>
|
|
</template>
|
|
|
|
<script setup>
|
|
import NestedPopover from '@/components/NestedPopover.vue'
|
|
import SortIcon from '@/components/Icons/SortIcon.vue'
|
|
import DragIcon from '@/components/Icons/DragIcon.vue'
|
|
import { useSortable } from '@vueuse/integrations/useSortable'
|
|
import { useOrderBy } from '@/composables/orderby'
|
|
import {
|
|
FeatherIcon,
|
|
Button,
|
|
Autocomplete,
|
|
FormControl,
|
|
createResource,
|
|
} from 'frappe-ui'
|
|
import { computed, ref, watch } from 'vue'
|
|
|
|
const props = defineProps({
|
|
doctype: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
})
|
|
|
|
const { get: getOrderBy, set: setOrderBy } = useOrderBy()
|
|
|
|
const sortButtonRef = ref(null)
|
|
const sortValues = ref(initialOrderBy())
|
|
|
|
const sortOptions = createResource({
|
|
url: 'crm.api.doc.sort_options',
|
|
auto: true,
|
|
params: {
|
|
doctype: props.doctype,
|
|
},
|
|
})
|
|
|
|
const options = computed(() => {
|
|
if (!sortOptions.data) return []
|
|
const selectedOptions = sortValues.value.map((sort) => sort.fieldname)
|
|
return sortOptions.data.filter((option) => {
|
|
return !selectedOptions.includes(option.value)
|
|
})
|
|
})
|
|
|
|
function initialOrderBy() {
|
|
const orderBy = getOrderBy()
|
|
if (!orderBy) return []
|
|
const sortOptions = orderBy.split(', ')
|
|
return sortOptions.map((sortOption) => {
|
|
const [fieldname, direction] = sortOption.split(' ')
|
|
return { fieldname, direction }
|
|
})
|
|
}
|
|
|
|
const sortSortable = useSortable('#sort-list', sortValues, {
|
|
handle: '.handle',
|
|
animation: 200,
|
|
})
|
|
|
|
watch(
|
|
() => sortValues.value,
|
|
(value) => {
|
|
const updatedSort = value
|
|
.map((sort) => {
|
|
const option = sortOptions.data.find((o) => o.value === sort.fieldname)
|
|
return `${option.value} ${sort.direction}`
|
|
})
|
|
.join(', ')
|
|
setOrderBy(updatedSort)
|
|
},
|
|
{
|
|
deep: true,
|
|
}
|
|
)
|
|
|
|
watch(
|
|
() => getOrderBy(),
|
|
(value) => {
|
|
if (!value) {
|
|
sortValues.value = []
|
|
}
|
|
}
|
|
)
|
|
|
|
function setSort(data) {
|
|
sortValues.value = [
|
|
...sortValues.value,
|
|
{ fieldname: data.value, direction: 'asc' },
|
|
]
|
|
sortSortable.start()
|
|
}
|
|
|
|
function updateSort(data, index) {
|
|
sortValues.value[index] = {
|
|
fieldname: data.value,
|
|
direction: sortValues.value[index].direction,
|
|
}
|
|
}
|
|
|
|
function removeSort(index) {
|
|
sortValues.value.splice(index, 1)
|
|
}
|
|
|
|
function clearSort(close) {
|
|
sortValues.value = []
|
|
close()
|
|
}
|
|
</script>
|