Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianWendelborn committed Dec 6, 2021
1 parent 9fcb627 commit 4dc8ea8
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 175 deletions.
Original file line number Diff line number Diff line change
@@ -1,198 +1,30 @@
<template>
<div>
<div ref="tippyTriggerRef">
<KtField
v-bind="{ field }"
class="kt-field-select kt-field-select--single-remote"
:getEmptyValue="() => null"
:helpTextSlot="$slots.helpText"
>
<input
v-bind="inputProps"
@blur="handleInputBlur"
@click="handleInputClick"
@focus="handleInputFocus"
@input="updateQuery"
/>
<template v-slot:actionIcon="{ classes, handleClear, showClear }">
<ActionIconNext
v-bind="{
classes,
handleClear,
isDropdownOpen,
showClear,
}"
/>
</template>
</KtField>
</div>
<div ref="tippyContentRef">
<FieldSelectOptions
:actions="actions"
:isDisabled="field.isDisabled"
:options="options"
:value="field.currentValue === null ? [] : [field.currentValue]"
@close="setIsDropdownOpen(false)"
@input="(e) => selectOption(e.length === 0 ? null : e[0])"
/>
</div>
</div>
<GenericSelectField isRemote />
</template>

<script lang="ts">
import { computed, defineComponent, ref, watch } from '@vue/composition-api'
import { defineComponent } from '@vue/composition-api'
import { KtField } from '../kotti-field'
import { KOTTI_FIELD_PROPS } from '../kotti-field/constants'
import { useField } from '../kotti-field/hooks'
import { useForceUpdate } from '../kotti-field/hooks'
import ActionIconNext from './components/ActionIconNext.vue'
import FieldSelectOptions from './components/Options.vue'
import {
KOTTI_FIELD_REMOTE_SINGLE_SELECT_PROPS,
KOTTI_FIELD_REMOTE_SELECT_SUPPORTS,
} from './constants'
import { useSelectTippy } from './hooks/use-select-tippy'
import { KottiFieldSingleSelectRemote } from './types'
const UPDATE_QUERY = 'update:query'
const isTippyOrInTippy = (element: Element, tippy: Element | null): boolean => {
if (tippy === null) return false
let currentElement = element
while (currentElement) {
if (currentElement.isSameNode(tippy)) return true
currentElement = currentElement?.parentElement
}
return false
}
const isEqualValue = (
currentValue: KottiFieldSingleSelectRemote.Value,
newValue: KottiFieldSingleSelectRemote.Value,
) => currentValue === newValue
import GenericSelectField from './components/GenericSelectField.vue'
import { KOTTI_FIELD_REMOTE_SINGLE_SELECT_PROPS } from './constants'
export default defineComponent({
name: 'KtFieldSingleSelectRemote',
components: {
ActionIconNext,
FieldSelectOptions,
KtField,
GenericSelectField,
},
props: {
...KOTTI_FIELD_PROPS,
...KOTTI_FIELD_REMOTE_SINGLE_SELECT_PROPS,
query: { default: null, type: String },
value: { default: null, type: [Number, String, Boolean] },
},
setup(props: KottiFieldSingleSelectRemote.Props, { emit }) {
const field = useField<KottiFieldSingleSelectRemote.Value, string | null>({
emit,
isCorrectDataType: (value): value is KottiFieldSingleSelectRemote.Value =>
['boolean', 'number', 'string', 'symbol'].includes(typeof value) ||
value === null,
isEmpty: (value) => value === null,
props,
supports: KOTTI_FIELD_REMOTE_SELECT_SUPPORTS,
})
const selectTippy = useSelectTippy()
const isInputFocused = ref(false)
const selectedLabel = computed((): string | null => {
if (field.currentValue === null) return null
return (
props.options.find((option) => option.value === field.currentValue)
?.label ?? null
)
})
const { forceUpdateKey, forceUpdate } = useForceUpdate()
const isUserInteracting = computed(
() => isInputFocused.value || selectTippy.isDropdownOpen.value,
)
watch(isUserInteracting, (newValue) => {
if (!newValue) emit(UPDATE_QUERY, null)
})
setup(_, { emit }) {
return {
field,
handleInputBlur: (event: { relatedTarget: HTMLElement }) => {
const blurToElement = event.relatedTarget
if (
!isTippyOrInTippy(blurToElement, selectTippy.tippyContentRef.value)
) {
selectTippy.setIsDropdownOpen(false)
}
isInputFocused.value = false
},
handleInputClick: () => {
// always show the dropdown if the input was clicked
selectTippy.setIsDropdownOpen(true)
},
handleInputFocus: () => {
// always show the dropdown if the input was focused
selectTippy.setIsDropdownOpen(true)
isInputFocused.value = true
},
inputProps: computed(() => ({
class: ['kt-field-select--single-remote__wrapper'],
forceUpdateKey: forceUpdateKey.value,
placeholder: props.placeholder ?? undefined,
size: 1,
type: 'text',
value: (() => {
if (isUserInteracting.value) return props.query ?? undefined
return selectedLabel.value ?? undefined
})(),
})),
isDropdownOpen: selectTippy.isDropdownOpen,
tippyContentRef: selectTippy.tippyContentRef,
tippyTriggerRef: selectTippy.tippyTriggerRef,
selectOption: (
value: KottiFieldSingleSelectRemote.Props['options'][number]['value'],
) => {
// optimization: no need to change the value if it’s already set
if (!isEqualValue(field.currentValue, value)) field.setValue(value)
// close the tippy instance whenever a selection is made
selectTippy.setIsDropdownOpen(false)
// when a selection is made, intentionally reset the query
// so that the api-call (for example) can already trigger
// and load the non-filtered options
emit(UPDATE_QUERY, null)
},
setIsDropdownOpen: selectTippy.setIsDropdownOpen,
updateQuery: (event: { target: HTMLInputElement }) => {
const newValue = event.target.value
emit(UPDATE_QUERY, newValue === '' ? null : newValue)
forceUpdate()
},
onEmit: emit,
}
},
})
</script>

<style lang="scss" scoped>
.kt-field-select--single-remote {
&__wrapper {
display: flex;
width: 100%;
padding: 0;
margin: 0;
line-height: 1.6;
border: 0;
}
}
</style>
Loading

0 comments on commit 4dc8ea8

Please sign in to comment.