140 lines
3.0 KiB
Vue
140 lines
3.0 KiB
Vue
<template>
|
|
<div class="space-y-1.5">
|
|
<label class="block" :class="labelClasses" v-if="attrs.label">
|
|
{{ attrs.label }}
|
|
</label>
|
|
<Autocomplete
|
|
ref="autocomplete"
|
|
:options="options.data"
|
|
v-model="value"
|
|
:size="attrs.size || 'sm'"
|
|
:variant="attrs.variant"
|
|
:placeholder="attrs.placeholder"
|
|
:filterable="false"
|
|
>
|
|
<template #target="{ open, togglePopover }">
|
|
<slot name="target" v-bind="{ open, togglePopover }" />
|
|
</template>
|
|
|
|
<template #prefix>
|
|
<slot name="prefix" />
|
|
</template>
|
|
|
|
<template #item-prefix="{ active, selected, option }">
|
|
<slot name="item-prefix" v-bind="{ active, selected, option }" />
|
|
</template>
|
|
|
|
<template #item-label="{ active, selected, option }">
|
|
<slot name="item-label" v-bind="{ active, selected, option }" />
|
|
</template>
|
|
|
|
<template v-if="attrs.onCreate" #footer="{ value, close }">
|
|
<div>
|
|
<Button
|
|
variant="ghost"
|
|
class="w-full !justify-start"
|
|
label="Create New"
|
|
@click="attrs.onCreate(value, close)"
|
|
>
|
|
<template #prefix>
|
|
<FeatherIcon name="plus" class="h-4" />
|
|
</template>
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
</Autocomplete>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
|
import { watchDebounced } from '@vueuse/core'
|
|
import { createResource } from 'frappe-ui'
|
|
import { useAttrs, computed, ref } from 'vue'
|
|
|
|
const props = defineProps({
|
|
doctype: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
modelValue: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue', 'change'])
|
|
|
|
const attrs = useAttrs()
|
|
|
|
const valuePropPassed = computed(() => 'value' in attrs)
|
|
|
|
const value = computed({
|
|
get: () => (valuePropPassed.value ? attrs.value : props.modelValue),
|
|
set: (val) => {
|
|
return (
|
|
val?.value &&
|
|
emit(valuePropPassed.value ? 'change' : 'update:modelValue', val?.value)
|
|
)
|
|
},
|
|
})
|
|
|
|
const autocomplete = ref(null)
|
|
const text = ref('')
|
|
|
|
watchDebounced(
|
|
() => autocomplete.value?.query,
|
|
(val) => {
|
|
val = val || ''
|
|
if (text.value === val) return
|
|
text.value = val
|
|
reload(val)
|
|
},
|
|
{ debounce: 300, immediate: true }
|
|
)
|
|
|
|
watchDebounced(
|
|
() => props.doctype,
|
|
() => reload(''),
|
|
{ debounce: 300, immediate: true }
|
|
)
|
|
|
|
const options = createResource({
|
|
url: 'frappe.desk.search.search_link',
|
|
cache: [props.doctype, text.value],
|
|
method: 'POST',
|
|
params: {
|
|
txt: text.value,
|
|
doctype: props.doctype,
|
|
},
|
|
transform: (data) => {
|
|
return data.map((option) => {
|
|
return {
|
|
label: option.value,
|
|
value: option.value,
|
|
}
|
|
})
|
|
},
|
|
})
|
|
|
|
function reload(val) {
|
|
options.update({
|
|
params: {
|
|
txt: val,
|
|
doctype: props.doctype,
|
|
},
|
|
})
|
|
options.reload()
|
|
}
|
|
|
|
const labelClasses = computed(() => {
|
|
return [
|
|
{
|
|
sm: 'text-xs',
|
|
md: 'text-base',
|
|
}[attrs.size || 'sm'],
|
|
'text-gray-600',
|
|
]
|
|
})
|
|
</script>
|