Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disabled select input fixes #355

Merged
merged 1 commit into from
Mar 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 31 additions & 40 deletions client/components/forms/components/VSelect.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
<template>
<div class="v-select relative" :class="[{'w-0': multiple, 'min-w-full':multiple}]" ref="select">
<div class="v-select relative" :class="[{ 'w-0': multiple, 'min-w-full': multiple }]" ref="select">
<span class="inline-block w-full rounded-md">
<button type="button" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label"
class="cursor-pointer"
:style="inputStyle"
:class="[theme.SelectInput.input,{'py-2': !multiple || loading,'py-1': multiple, '!ring-red-500 !ring-2 !border-transparent': hasError, '!cursor-not-allowed !bg-gray-200': disabled}, inputClass]"
@click="toggleDropdown"
>
<div :class="{'h-6': !multiple, 'min-h-8': multiple && !loading}">
class="cursor-pointer" :style="inputStyle"
:class="[theme.SelectInput.input, { 'py-2': !multiple || loading, 'py-1': multiple, '!ring-red-500 !ring-2 !border-transparent': hasError, '!cursor-not-allowed !bg-gray-200': disabled }, inputClass]"
@click="toggleDropdown">
<div :class="{ 'h-6': !multiple, 'min-h-8': multiple && !loading }">
<transition name="fade" mode="out-in">
<Loader v-if="loading" key="loader" class="h-6 w-6 text-nt-blue mx-auto" />
<div v-else-if="modelValue" key="value" class="flex" :class="{'min-h-8': multiple}">
<div v-else-if="modelValue" key="value" class="flex" :class="{ 'min-h-8': multiple }">
<slot name="selected" :option="modelValue" />
</div>
<div v-else key="placeholder">
<slot name="placeholder">
<div class="text-gray-400 dark:text-gray-500 w-full text-left truncate pr-3"
:class="{'py-1': multiple && !loading}"
>
:class="{ 'py-1': multiple && !loading }">
{{ placeholder }}
</div>
</slot>
Expand All @@ -32,38 +29,31 @@
</button>
</span>
<collapsible v-model="isOpen" @click-away="onClickAway"
class="absolute mt-1 rounded-md bg-white dark:bg-notion-dark-light shadow-xl z-10"
:class="dropdownClass"
>
class="absolute mt-1 rounded-md bg-white dark:bg-notion-dark-light shadow-xl z-10" :class="dropdownClass">
<ul tabindex="-1" role="listbox"
class="rounded-md text-base leading-6 shadow-xs overflow-auto focus:outline-none sm:text-sm sm:leading-5 relative"
:class="{'max-h-42 py-1': !isSearchable,'max-h-48 pb-1': isSearchable}"
>
class="rounded-md text-base leading-6 shadow-xs overflow-auto focus:outline-none sm:text-sm sm:leading-5 relative"
:class="{ 'max-h-42 py-1': !isSearchable, 'max-h-48 pb-1': isSearchable }">
<div v-if="isSearchable" class="px-2 pt-2 sticky top-0 bg-white dark-bg-notion-dark-light z-10">
<text-input v-model="searchTerm" name="search" :color="color" :theme="theme"
placeholder="Search..."
/>
<text-input v-model="searchTerm" name="search" :color="color" :theme="theme" placeholder="Search..." />
</div>
<div v-if="loading" class="w-full py-2 flex justify-center">
<Loader class="h-6 w-6 text-nt-blue mx-auto" />
</div>
<template v-if="filteredOptions.length > 0">
<li v-for="item in filteredOptions" :key="item[optionKey]" role="option" :style="optionStyle"
:class="{'px-3 pr-9': multiple, 'px-3': !multiple}"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus-text-white focus-nt-blue"
@click="select(item)"
>
:class="{ 'px-3 pr-9': multiple, 'px-3': !multiple }"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus-text-white focus-nt-blue"
@click="select(item)">
<slot name="option" :option="item" :selected="isSelected(item)" />
</li>
</template>
<p v-else-if="!loading && !(allowCreation && searchTerm)" class="w-full text-gray-500 text-center py-2">
{{ (allowCreation ? 'Type something to add an option' : 'No option available') }}.
</p>
<li v-if="allowCreation && searchTerm" role="option" :style="optionStyle"
:class="{'px-3 pr-9': multiple, 'px-3': !multiple}"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus-text-white focus-nt-blue"
@click="createOption(searchTerm)"
>
:class="{ 'px-3 pr-9': multiple, 'px-3': !multiple }"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus-text-white focus-nt-blue"
@click="createOption(searchTerm)">
Create <b class="px-1 bg-gray-300 rounded group-hover-text-black">{{ searchTerm }}</b>
</li>
</ul>
Expand Down Expand Up @@ -103,31 +93,31 @@ export default {
allowCreation: { type: Boolean, default: false },
disabled: { type: Boolean, default: false }
},
data () {
data() {
return {
isOpen: false,
searchTerm: '',
defaultValue: this.modelValue ?? null
}
},
computed: {
optionStyle () {
optionStyle() {
return {
'--bg-form-color': this.color
}
},
inputStyle () {
inputStyle() {
return {
'--tw-ring-color': this.color
}
},
debouncedRemote () {
debouncedRemote() {
if (this.remote) {
return debounce(this.remote, 300)
}
return null
},
filteredOptions () {
filteredOptions() {
if (!this.data) return []
if (!this.searchable || this.remote || this.searchTerm === '') {
return this.data
Expand All @@ -142,26 +132,26 @@ export default {
return res.item
})
},
isSearchable () {
isSearchable() {
return this.searchable || this.remote !== null || this.allowCreation
}
},
watch: {
searchTerm (val) {
searchTerm(val) {
if (!this.debouncedRemote) return
if ((this.remote && val) || (val === '' && !this.modelValue) || (val === '' && this.isOpen)) {
return this.debouncedRemote(val)
}
}
},
methods: {
onClickAway (event) {
onClickAway(event) {
// Check that event target isn't children of dropdown
if (this.$refs.select && !this.$refs.select.contains(event.target)) {
this.isOpen = false
}
},
isSelected (value) {
isSelected(value) {
if (!this.modelValue) return false

if (this.emitKey && value[this.emitKey]) {
Expand All @@ -173,16 +163,17 @@ export default {
}
return this.modelValue === value
},
toggleDropdown () {
toggleDropdown() {
if (this.disabled) {
this.isOpen = false
} else {
this.isOpen = !this.isOpen
}
this.isOpen = !this.isOpen
if (!this.isOpen) {
this.searchTerm = ''
}
},
select (value) {
select(value) {
if (!this.multiple) {
// Close after select
this.toggleDropdown()
Expand Down Expand Up @@ -215,7 +206,7 @@ export default {
}
}
},
createOption (newOption) {
createOption(newOption) {
if (newOption) {
const newItem = {
name: newOption,
Expand Down
Loading