You've already forked AstralRinth
forked from didirus/AstralRinth
Switch to composition API, Add custom names (#47)
* Switch to composition API, Add custom names * Update package.json * Update DropdownSelect.vue
This commit is contained in:
@@ -23,6 +23,12 @@ const value = ref(null)
|
|||||||
placeholder="Choose Frequency"
|
placeholder="Choose Frequency"
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
|
<DropdownSelect
|
||||||
|
v-model="value"
|
||||||
|
:options="['Daily', 'Weekly', 'Monthly', 'Tomorrow', 'Yesterday', 'Today', 'Biweekly', 'Tuesday', 'January']"
|
||||||
|
placeholder="Choose Frequency"
|
||||||
|
:display-name="(name) => name?.toUpperCase()"
|
||||||
|
/>
|
||||||
</DemoContainer>
|
</DemoContainer>
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
:value="option"
|
:value="option"
|
||||||
:name="name"
|
:name="name"
|
||||||
/>
|
/>
|
||||||
<label :for="`${name}-${index}`">{{ option }}</label>
|
<label :for="`${name}-${index}`">{{ displayName(option) }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
@@ -59,113 +59,129 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { computed, ref, watch } from 'vue'
|
||||||
props: {
|
|
||||||
options: {
|
const props = defineProps({
|
||||||
type: Array,
|
options: {
|
||||||
required: true,
|
type: Array,
|
||||||
},
|
required: true,
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
defaultValue: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
renderUp: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
emits: ['input', 'change', 'update:modelValue'],
|
name: {
|
||||||
data() {
|
type: String,
|
||||||
return {
|
required: true,
|
||||||
dropdownVisible: false,
|
},
|
||||||
selectedValue: this.modelValue || this.defaultValue,
|
defaultValue: {
|
||||||
focusedOptionIndex: null,
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
renderUp: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
displayName: {
|
||||||
|
type: Function,
|
||||||
|
default: (option) => option,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['input', 'change', 'update:modelValue'])
|
||||||
|
|
||||||
|
const dropdownVisible = ref(false)
|
||||||
|
const selectedValue = ref(props.modelValue || props.defaultValue)
|
||||||
|
const focusedOptionIndex = ref(null)
|
||||||
|
const dropdown = ref(null)
|
||||||
|
const optionElements = ref(null)
|
||||||
|
|
||||||
|
const selectedOption = computed(() => {
|
||||||
|
return props.displayName(selectedValue.value) || props.placeholder || 'Select an option'
|
||||||
|
})
|
||||||
|
|
||||||
|
const radioValue = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue || selectedValue.value
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
emit('update:modelValue', newValue)
|
||||||
|
selectedValue.value = newValue
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newValue) => {
|
||||||
|
selectedValue.value = newValue
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggleDropdown = () => {
|
||||||
|
if (!props.disabled) {
|
||||||
|
dropdownVisible.value = !dropdownVisible.value
|
||||||
|
dropdown.value.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectOption = (option, index) => {
|
||||||
|
radioValue.value = option
|
||||||
|
emit('change', { option, index })
|
||||||
|
dropdownVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFocus = () => {
|
||||||
|
if (!props.disabled) {
|
||||||
|
focusedOptionIndex.value = props.options.findIndex((option) => option === selectedValue.value)
|
||||||
|
dropdownVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onBlur = (event) => {
|
||||||
|
if (!isChildOfDropdown(event.relatedTarget)) {
|
||||||
|
dropdownVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const focusPreviousOption = () => {
|
||||||
|
if (!props.disabled) {
|
||||||
|
if (!dropdownVisible.value) {
|
||||||
|
toggleDropdown()
|
||||||
}
|
}
|
||||||
},
|
focusedOptionIndex.value =
|
||||||
computed: {
|
(focusedOptionIndex.value + props.options.length - 1) % props.options.length
|
||||||
selectedOption() {
|
optionElements.value[focusedOptionIndex.value].focus()
|
||||||
return this.selectedValue || this.placeholder || 'Select an option'
|
}
|
||||||
},
|
}
|
||||||
radioValue: {
|
|
||||||
get() {
|
const focusNextOptionOrOpen = () => {
|
||||||
return this.modelValue || this.selectedValue
|
if (!props.disabled) {
|
||||||
},
|
if (!dropdownVisible.value) {
|
||||||
set(newValue) {
|
toggleDropdown()
|
||||||
this.$emit('update:modelValue', newValue)
|
}
|
||||||
this.selectedValue = newValue
|
focusedOptionIndex.value = (focusedOptionIndex.value + 1) % props.options.length
|
||||||
},
|
optionElements.value[focusedOptionIndex.value].focus()
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
methods: {
|
|
||||||
toggleDropdown() {
|
const isChildOfDropdown = (element) => {
|
||||||
if (!this.disabled) {
|
let currentNode = element
|
||||||
this.dropdownVisible = !this.dropdownVisible
|
while (currentNode) {
|
||||||
this.$refs.dropdown.focus()
|
if (currentNode === this.$el) {
|
||||||
}
|
return true
|
||||||
},
|
}
|
||||||
selectOption(option, index) {
|
currentNode = currentNode.parentNode
|
||||||
this.radioValue = option
|
}
|
||||||
this.$emit('change', { option, index })
|
return false
|
||||||
this.dropdownVisible = false
|
|
||||||
},
|
|
||||||
onFocus() {
|
|
||||||
if (!this.disabled) {
|
|
||||||
this.focusedOptionIndex = this.options.findIndex((option) => option === this.selectedValue)
|
|
||||||
this.dropdownVisible = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onBlur(event) {
|
|
||||||
if (!this.isChildOfDropdown(event.relatedTarget)) {
|
|
||||||
this.dropdownVisible = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
focusPreviousOption() {
|
|
||||||
if (!this.disabled) {
|
|
||||||
if (!this.dropdownVisible) {
|
|
||||||
this.toggleDropdown()
|
|
||||||
}
|
|
||||||
this.focusedOptionIndex =
|
|
||||||
(this.focusedOptionIndex + this.options.length - 1) % this.options.length
|
|
||||||
this.$refs.optionElements[this.focusedOptionIndex].focus()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
focusNextOptionOrOpen() {
|
|
||||||
if (!this.disabled) {
|
|
||||||
if (!this.dropdownVisible) {
|
|
||||||
this.toggleDropdown()
|
|
||||||
}
|
|
||||||
this.focusedOptionIndex = (this.focusedOptionIndex + 1) % this.options.length
|
|
||||||
this.$refs.optionElements[this.focusedOptionIndex].focus()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isChildOfDropdown(element) {
|
|
||||||
let currentNode = element
|
|
||||||
while (currentNode) {
|
|
||||||
if (currentNode === this.$el) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
currentNode = currentNode.parentNode
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "omorphia",
|
"name": "omorphia",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.4.12",
|
"version": "0.4.13",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user