Created
October 26, 2022 17:07
-
-
Save iamandrewluca/3b6bc1930efe9239edc6b83dcf714d56 to your computer and use it in GitHub Desktop.
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
<template> | |
<ComboboxOption as="template" :value="option" v-slot="{ selected, active }"> | |
<li class="relative cursor-default select-none py-2 pl-10 pr-4" :class="{ | |
'bg-teal-600 text-white': active, | |
'text-gray-900': !active, | |
}"> | |
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }"> | |
{{ option.name }} | |
</span> | |
<span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3" | |
:class="{ 'text-white': active, 'text-teal-600': !active }"> | |
<CheckIcon class="h-5 w-5" aria-hidden="true" /> | |
</span> | |
</li> | |
</ComboboxOption> | |
</template> | |
<script lang="ts" setup> | |
import { CheckIcon } from '@heroicons/vue/20/solid' | |
import { ComboboxOption } from '@headlessui/vue'; | |
defineProps({ | |
option: Object, | |
}) | |
</script> |
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
<template> | |
<ComboboxOption as="template" :value="option" v-slot="{ selected, active }"> | |
<li class="relative cursor-default select-none py-2 pl-10 pr-4" :class="{ | |
'bg-teal-600 text-white': active, | |
'text-gray-900': !active, | |
}"> | |
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }"> | |
{{ option.name }} | |
</span> | |
<span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3" | |
:class="{ 'text-white': active, 'text-teal-600': !active }"> | |
<CheckIcon class="h-5 w-5" aria-hidden="true" /> | |
</span> | |
</li> | |
</ComboboxOption> | |
</template> | |
<script lang="ts" setup> | |
import { CheckIcon } from '@heroicons/vue/20/solid' | |
import { ComboboxOption } from '@headlessui/vue'; | |
const props = defineProps({ | |
option: Object, | |
}) | |
</script> |
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
<template> | |
<Combobox :model-value="modelValue" @update:model-value="emit('update:modelValue', $event)" :multiple="multiple"> | |
<div class="relative my-4"> | |
<div | |
class="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm"> | |
<ComboboxInput class="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0" | |
:displayValue="(option) => (option as any)?.name" @change="query = $event.target.value" /> | |
<ComboboxButton class="absolute inset-y-0 right-0 flex items-center pr-2"> | |
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" /> | |
</ComboboxButton> | |
</div> | |
<TransitionRoot leave="transition ease-in duration-100" leaveFrom="opacity-100" leaveTo="opacity-0" | |
@after-leave="query = ''"> | |
<ComboboxOptions | |
class="absolute z-40 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> | |
<div v-if="filteredoptions.length === 0 && query !== ''" | |
class="relative cursor-default select-none py-2 px-4 text-gray-700"> | |
Nothing found. | |
</div> | |
<ComboItem v-for="option in filteredoptions" :key="option.id" :option="option" /> | |
</ComboboxOptions> | |
</TransitionRoot> | |
</div> | |
</Combobox> | |
</template> | |
<script setup lang="ts"> | |
import { ref, computed, PropType } from 'vue' | |
import { | |
Combobox, | |
ComboboxInput, | |
ComboboxButton, | |
ComboboxOptions, | |
TransitionRoot, | |
} from '@headlessui/vue' | |
import { ChevronUpDownIcon } from '@heroicons/vue/20/solid' | |
import ComboMultiple from './combo-multiple.vue'; | |
import ComboSingle from './combo-single.vue'; | |
const emit = defineEmits(['update:modelValue']) | |
const props = defineProps({ | |
options: { | |
type: Array as PropType<Array<{ id: number; name: string }>>, | |
default: () => [] | |
}, | |
modelValue: { | |
type: Object as PropType<{ id: number; name: string }>, | |
default: () => undefined | |
}, | |
multiple: { | |
type: Boolean, | |
default: () => false | |
} | |
}) | |
const ComboItem = props.multiple ? ComboMultiple : ComboSingle | |
let query = ref('') | |
let filteredoptions = computed(() => | |
query.value === '' | |
? props.options | |
: props.options.filter((option) => | |
option.name | |
.toLowerCase() | |
.replace(/\s+/g, '') | |
.includes(query.value.toLowerCase().replace(/\s+/g, '')) | |
) | |
) | |
</script> |
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
<template> | |
<form @submit="onSubmit"> | |
<template v-for="(questionData, index) in mappedData"> | |
<Combo :options="questionData.answers" :multiple="questionData.multiple" v-model="answers[index].value as any" /> | |
<div v-if="isOther(answers[index].value)" | |
class="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm"> | |
<input class="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0" | |
v-model="inputAnswers[questionData.question].value" /> | |
</div> | |
</template> | |
<button type="submit">Submit</button> | |
</form> | |
</template> | |
<script lang="ts" setup> | |
import Combo from './combo.vue'; | |
const serverData = [ | |
{ | |
question: 'Question 1', | |
multiple: true, | |
answers: [ | |
{ uuid: 1, value: 'Wade Cooper' }, | |
{ uuid: 2, value: 'Arlene Mccoy' }, | |
{ uuid: 3, value: 'Devon Webb' }, | |
{ uuid: 4, value: 'Tom Cook' }, | |
{ uuid: 5, value: 'Tanya Fox' }, | |
{ uuid: 6, value: 'Hellen Schmidt' }, | |
{ uuid: 7, value: 'Other' }, | |
] | |
}, | |
{ | |
question: 'Question 2', | |
multiple: false, | |
answers: [ | |
{ uuid: 1, value: 'Wade Cooper' }, | |
{ uuid: 2, value: 'Arlene Mccoy' }, | |
{ uuid: 3, value: 'Devon Webb' }, | |
{ uuid: 4, value: 'Tom Cook' }, | |
{ uuid: 5, value: 'Tanya Fox' }, | |
{ uuid: 6, value: 'Hellen Schmidt' }, | |
{ uuid: 7, value: 'Other' }, | |
] | |
}, | |
{ | |
question: 'Question 3', | |
multiple: true, | |
answers: [ | |
{ uuid: 1, value: 'Wade Cooper' }, | |
{ uuid: 2, value: 'Arlene Mccoy' }, | |
{ uuid: 3, value: 'Devon Webb' }, | |
{ uuid: 4, value: 'Tom Cook' }, | |
{ uuid: 5, value: 'Tanya Fox' }, | |
{ uuid: 6, value: 'Hellen Schmidt' }, | |
] | |
}, | |
] | |
const mappedData = serverData.map(Q => ({ | |
...Q, | |
answers: Q.answers.map(a => ({ id: a.uuid, name: a.value })) | |
})) | |
const onlyOther = mappedData.filter(Q => isOther(Q.answers)) | |
const otherEntries = onlyOther.map((Q) => [Q.question, ref()]) | |
const inputAnswers = Object.fromEntries(otherEntries) | |
const answers = mappedData.map(q => ref(q.multiple ? [] : undefined)) | |
function onSubmit(e: Event) { | |
e.preventDefault() | |
const combinedData = [] | |
for (let i = 0; i < serverData.length; i++) { | |
const mapped = mappedData[i] | |
const whatWasFromServer = serverData[i] | |
const answer = toRaw(unref(answers[i])) | |
const otherAnswer = isOther(mapped.answers) | |
? toRaw(unref(inputAnswers[whatWasFromServer.question])) | |
: undefined | |
combinedData.push({ whatWasFromServer, answer, otherAnswer }) | |
} | |
console.log(combinedData); | |
} | |
function isOther(option: any) { | |
return Array.isArray(option) | |
? option.some(isOther) | |
: option?.name === 'Other' | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment