forked from opentiny/tiny-vue
styles(autocomplete): [autocomplete] add autocomplete types (#1322)
This commit is contained in:
parent
fc10ea4cf6
commit
e917e54df0
|
@ -10,8 +10,26 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import type {
|
||||
IAutoCompleteProps,
|
||||
IAutoCompleteState,
|
||||
IAutoCompleteApi,
|
||||
IAutoCompleteConstants,
|
||||
IAutoCompleteRenderlessParamUtils
|
||||
} from '@/types'
|
||||
|
||||
export const getData =
|
||||
({ props, state, updatePopper, nextTick }) =>
|
||||
({
|
||||
props,
|
||||
state,
|
||||
updatePopper,
|
||||
nextTick
|
||||
}: {
|
||||
props: IAutoCompleteProps
|
||||
state: IAutoCompleteState
|
||||
updatePopper: (popperElm?: HTMLElement | undefined) => void
|
||||
nextTick: IAutoCompleteRenderlessParamUtils['nextTick']
|
||||
}) =>
|
||||
(queryString) => {
|
||||
if (state.suggestionDisabled) {
|
||||
return
|
||||
|
@ -19,7 +37,7 @@ export const getData =
|
|||
|
||||
state.loading = true
|
||||
|
||||
props.fetchSuggestions(queryString, (suggestions) => {
|
||||
props?.fetchSuggestions?.(queryString, (suggestions) => {
|
||||
state.loading = false
|
||||
|
||||
if (state.suggestionDisabled) {
|
||||
|
@ -38,7 +56,17 @@ export const getData =
|
|||
}
|
||||
|
||||
export const handleChange =
|
||||
({ api, emit, state, props }) =>
|
||||
({
|
||||
api,
|
||||
emit,
|
||||
state,
|
||||
props
|
||||
}: {
|
||||
api: IAutoCompleteApi
|
||||
emit: IAutoCompleteRenderlessParamUtils['emit']
|
||||
state: IAutoCompleteState
|
||||
props: IAutoCompleteProps
|
||||
}) =>
|
||||
(value) => {
|
||||
state.activated = true
|
||||
emit('update:modelValue', value)
|
||||
|
@ -55,7 +83,17 @@ export const handleChange =
|
|||
}
|
||||
|
||||
export const handleFocus =
|
||||
({ api, emit, props, state }) =>
|
||||
({
|
||||
api,
|
||||
emit,
|
||||
props,
|
||||
state
|
||||
}: {
|
||||
api: IAutoCompleteApi
|
||||
emit: IAutoCompleteRenderlessParamUtils['emit']
|
||||
state: IAutoCompleteState
|
||||
props: IAutoCompleteProps
|
||||
}) =>
|
||||
(event) => {
|
||||
state.activated = true
|
||||
emit('focus', event)
|
||||
|
@ -67,25 +105,37 @@ export const handleFocus =
|
|||
}
|
||||
|
||||
export const handleBlur =
|
||||
({ emit, state }) =>
|
||||
({ emit, state }: { emit: IAutoCompleteRenderlessParamUtils['emit']; state: IAutoCompleteState }) =>
|
||||
(event) => {
|
||||
state.suggestionDisabled = true
|
||||
emit('blur', event)
|
||||
}
|
||||
|
||||
export const handleClear =
|
||||
({ emit, state }) =>
|
||||
({ emit, state }: { emit: IAutoCompleteRenderlessParamUtils['emit']; state: IAutoCompleteState }) =>
|
||||
() => {
|
||||
state.activated = false
|
||||
emit('clear')
|
||||
}
|
||||
|
||||
export const close = (state) => () => {
|
||||
export const close = (state: IAutoCompleteState) => () => {
|
||||
state.activated = false
|
||||
}
|
||||
|
||||
export const handleKeyEnter =
|
||||
({ api, emit, nextTick, props, state }) =>
|
||||
({
|
||||
api,
|
||||
emit,
|
||||
nextTick,
|
||||
props,
|
||||
state
|
||||
}: {
|
||||
api: IAutoCompleteApi
|
||||
emit: IAutoCompleteRenderlessParamUtils['emit']
|
||||
nextTick: IAutoCompleteRenderlessParamUtils['nextTick']
|
||||
props: IAutoCompleteProps
|
||||
state: IAutoCompleteState
|
||||
}) =>
|
||||
(event) => {
|
||||
if (state.suggestionVisible && state.highlightedIndex >= 0 && state.highlightedIndex < state.suggestions.length) {
|
||||
event.preventDefault()
|
||||
|
@ -101,7 +151,17 @@ export const handleKeyEnter =
|
|||
}
|
||||
|
||||
export const select =
|
||||
({ emit, nextTick, props, state }) =>
|
||||
({
|
||||
emit,
|
||||
nextTick,
|
||||
props,
|
||||
state
|
||||
}: {
|
||||
emit: IAutoCompleteRenderlessParamUtils['emit']
|
||||
nextTick: IAutoCompleteRenderlessParamUtils['nextTick']
|
||||
props: IAutoCompleteProps
|
||||
state: IAutoCompleteState
|
||||
}) =>
|
||||
(item) => {
|
||||
emit('update:modelValue', item[props.valueKey])
|
||||
emit('select', item)
|
||||
|
@ -114,7 +174,15 @@ export const select =
|
|||
}
|
||||
|
||||
export const highlight =
|
||||
({ constants, refs, state }) =>
|
||||
({
|
||||
constants,
|
||||
refs,
|
||||
state
|
||||
}: {
|
||||
constants: IAutoCompleteConstants
|
||||
refs: IAutoCompleteRenderlessParamUtils['refs']
|
||||
state: IAutoCompleteState
|
||||
}) =>
|
||||
(index) => {
|
||||
if (!state.suggestionVisible || state.loading) {
|
||||
return
|
||||
|
@ -150,7 +218,7 @@ export const highlight =
|
|||
$input.setAttribute('aria-activedescendant', `${state.id}-item-${state.highlightedIndex}`)
|
||||
}
|
||||
|
||||
export const computedVisible = (state) => {
|
||||
export const computedVisible = (state: IAutoCompleteState) => {
|
||||
const suggestions = state.suggestions
|
||||
let isValidData = Array.isArray(suggestions) && suggestions.length > 0
|
||||
|
||||
|
@ -158,7 +226,13 @@ export const computedVisible = (state) => {
|
|||
}
|
||||
|
||||
export const watchVisible =
|
||||
({ suggestionState, refs }) =>
|
||||
({
|
||||
suggestionState,
|
||||
refs
|
||||
}: {
|
||||
suggestionState: IAutoCompleteApi['suggestionState']
|
||||
refs: IAutoCompleteRenderlessParamUtils['refs']
|
||||
}) =>
|
||||
(val) => {
|
||||
let $input = refs.input.getInput()
|
||||
|
||||
|
@ -169,7 +243,15 @@ export const watchVisible =
|
|||
}
|
||||
|
||||
export const mounted =
|
||||
({ refs, state, suggestionState }) =>
|
||||
({
|
||||
refs,
|
||||
state,
|
||||
suggestionState
|
||||
}: {
|
||||
refs: IAutoCompleteRenderlessParamUtils['refs']
|
||||
state: IAutoCompleteState
|
||||
suggestionState: IAutoCompleteApi['suggestionState']
|
||||
}) =>
|
||||
() => {
|
||||
const input = refs.input
|
||||
const $input = input.getInput()
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
import debounce from '../common/deps/debounce'
|
||||
import userPopper from '../common/deps/vue-popper'
|
||||
import type { Ref } from 'vue'
|
||||
import { guid } from '../common/string'
|
||||
import {
|
||||
computedVisible,
|
||||
|
@ -27,6 +28,13 @@ import {
|
|||
select,
|
||||
highlight
|
||||
} from './index'
|
||||
import type {
|
||||
IAutoCompleteProps,
|
||||
IAutoCompleteState,
|
||||
IAutoCompleteApi,
|
||||
ISharedRenderlessFunctionParams,
|
||||
IAutoCompleteRenderlessParamUtils
|
||||
} from '@/types'
|
||||
|
||||
export const api = [
|
||||
'state',
|
||||
|
@ -44,8 +52,16 @@ export const api = [
|
|||
'doDestroy'
|
||||
]
|
||||
|
||||
const initState = ({ reactive, $prefix, computed }) => {
|
||||
const state = reactive({
|
||||
const initState = ({
|
||||
reactive,
|
||||
$prefix,
|
||||
computed
|
||||
}: {
|
||||
reactive: ISharedRenderlessFunctionParams<null>['reactive']
|
||||
$prefix: string
|
||||
computed: ISharedRenderlessFunctionParams<null>['computed']
|
||||
}) => {
|
||||
const state = reactive<IAutoCompleteState>({
|
||||
activated: false,
|
||||
suggestions: [],
|
||||
loading: false,
|
||||
|
@ -55,10 +71,22 @@ const initState = ({ reactive, $prefix, computed }) => {
|
|||
suggestionVisible: computed(() => computedVisible(state))
|
||||
})
|
||||
|
||||
return state
|
||||
return state as IAutoCompleteState
|
||||
}
|
||||
|
||||
const initSuggestionState = ({ reactive, parent, showPopper, popperElm, referenceElm }) =>
|
||||
export const initSuggestionState = ({
|
||||
reactive,
|
||||
parent,
|
||||
showPopper,
|
||||
popperElm,
|
||||
referenceElm
|
||||
}: {
|
||||
reactive: ISharedRenderlessFunctionParams<null>['reactive']
|
||||
parent: IAutoCompleteRenderlessParamUtils['parent']
|
||||
showPopper: Ref<boolean>
|
||||
popperElm: Ref<HTMLElement>
|
||||
referenceElm: Ref<HTMLElement>
|
||||
}) =>
|
||||
reactive({
|
||||
parent,
|
||||
dropdownWidth: '',
|
||||
|
@ -89,11 +117,19 @@ const initApi = ({ api, state, doDestroy, suggestionState, emit, refs, props, up
|
|||
}
|
||||
|
||||
export const renderless = (
|
||||
props,
|
||||
{ computed, onBeforeUnmount, onMounted, reactive, watch, toRefs, onDeactivated },
|
||||
{ $prefix, refs, parent, emit, constants, nextTick, slots }
|
||||
props: IAutoCompleteProps,
|
||||
{
|
||||
computed,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
reactive,
|
||||
watch,
|
||||
toRefs,
|
||||
onDeactivated
|
||||
}: ISharedRenderlessFunctionParams<null>,
|
||||
{ $prefix, refs, parent, emit, constants, nextTick, slots }: IAutoCompleteRenderlessParamUtils
|
||||
) => {
|
||||
const api = {}
|
||||
const api: Partial<IAutoCompleteApi> = {}
|
||||
const state = initState({ reactive, $prefix, computed })
|
||||
|
||||
const { showPopper, popperElm, referenceElm, doDestroy, updatePopper } = userPopper({
|
||||
|
@ -107,15 +143,15 @@ export const renderless = (
|
|||
onBeforeUnmount,
|
||||
toRefs,
|
||||
onDeactivated
|
||||
})
|
||||
} as any)
|
||||
|
||||
const suggestionState = initSuggestionState({ reactive, parent, showPopper, popperElm, referenceElm })
|
||||
|
||||
initApi({ api, state, doDestroy, suggestionState, emit, refs, props, updatePopper, nextTick, constants })
|
||||
|
||||
watch(() => state.suggestionVisible, api.watchVisible)
|
||||
watch(() => state.suggestionVisible, (api as IAutoCompleteApi).watchVisible)
|
||||
|
||||
onMounted(api.mounted)
|
||||
onMounted((api as IAutoCompleteApi).mounted)
|
||||
|
||||
return api
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import type { ExtractPropTypes, ComputedRef } from 'vue'
|
||||
import type { autoCompleteProps } from '@/autocomplete/src'
|
||||
import type { initSuggestionState } from '../src/autocomplete/vue'
|
||||
import type {
|
||||
watchVisible,
|
||||
mounted,
|
||||
handleChange,
|
||||
handleFocus,
|
||||
handleBlur,
|
||||
handleClear,
|
||||
close,
|
||||
handleKeyEnter,
|
||||
select,
|
||||
highlight
|
||||
} from '../src/autocomplete'
|
||||
import type { ISharedRenderlessParamUtils } from './shared.type'
|
||||
|
||||
export interface IAutoCompleteState {
|
||||
activated: boolean
|
||||
suggestions: unknown[]
|
||||
loading: boolean
|
||||
highlightedIndex: number
|
||||
suggestionDisabled: boolean
|
||||
id: string
|
||||
suggestionVisible: ComputedRef<boolean>
|
||||
}
|
||||
export type IAutoCompleteProps = ExtractPropTypes<typeof autoCompleteProps>
|
||||
export interface IAutoCompleteApi {
|
||||
state: IAutoCompleteState
|
||||
doDestroy: (forceDestroy?: boolean | undefined) => void
|
||||
suggestionState: ReturnType<typeof initSuggestionState>
|
||||
close: ReturnType<typeof close>
|
||||
handleBlur: ReturnType<typeof handleBlur>
|
||||
mounted: ReturnType<typeof mounted>
|
||||
highlight: ReturnType<typeof highlight>
|
||||
handleClear: ReturnType<typeof handleClear>
|
||||
select: ReturnType<typeof select>
|
||||
watchVisible: ReturnType<typeof watchVisible>
|
||||
handleChange: ReturnType<typeof handleChange>
|
||||
handleFocus: ReturnType<typeof handleFocus>
|
||||
handleKeyEnter: ReturnType<typeof handleKeyEnter>
|
||||
debouncedGetData: Function
|
||||
}
|
||||
|
||||
export type IAutoCompleteConstants = ReturnType<typeof autoCompleteProps._constants.default>
|
||||
|
||||
export type IAutoCompleteRenderlessParamUtils = ISharedRenderlessParamUtils<IAutoCompleteConstants>
|
|
@ -16,68 +16,69 @@ const $constants = {
|
|||
WARP_CLS: '.tiny-autocomplete-suggestion__wrap',
|
||||
ITEM_CLS: '.tiny-autocomplete-suggestion__list li'
|
||||
}
|
||||
export const autoCompleteProps = {
|
||||
...$props,
|
||||
_constants: {
|
||||
type: Object,
|
||||
default: () => $constants
|
||||
},
|
||||
autofocus: Boolean,
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
customItem: String,
|
||||
debounce: {
|
||||
type: Number,
|
||||
default: () => 300
|
||||
},
|
||||
disabled: Boolean,
|
||||
fetchSuggestions: Function,
|
||||
hideLoading: Boolean,
|
||||
highlightFirstItem: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
label: String,
|
||||
maxlength: Number,
|
||||
minlength: Number,
|
||||
modelValue: String,
|
||||
name: String,
|
||||
placeholder: String,
|
||||
placement: {
|
||||
type: String,
|
||||
default: () => 'bottom-start'
|
||||
},
|
||||
popperAppendToBody: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
popperClass: String,
|
||||
popperOptions: Object,
|
||||
prefixIcon: [String, Object],
|
||||
selectWhenUnmatched: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
size: String,
|
||||
suffixIcon: [String, Object],
|
||||
triggerOnFocus: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: () => 'value'
|
||||
},
|
||||
displayOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: $prefix + 'Autocomplete',
|
||||
props: {
|
||||
...$props,
|
||||
_constants: {
|
||||
type: Object,
|
||||
default: () => $constants
|
||||
},
|
||||
autofocus: Boolean,
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
customItem: String,
|
||||
debounce: {
|
||||
type: Number,
|
||||
default: () => 300
|
||||
},
|
||||
disabled: Boolean,
|
||||
fetchSuggestions: Function,
|
||||
hideLoading: Boolean,
|
||||
highlightFirstItem: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
label: String,
|
||||
maxlength: Number,
|
||||
minlength: Number,
|
||||
modelValue: String,
|
||||
name: String,
|
||||
placeholder: String,
|
||||
placement: {
|
||||
type: String,
|
||||
default: () => 'bottom-start'
|
||||
},
|
||||
popperAppendToBody: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
popperClass: String,
|
||||
popperOptions: Object,
|
||||
prefixIcon: [String, Object],
|
||||
selectWhenUnmatched: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
size: String,
|
||||
suffixIcon: [String, Object],
|
||||
triggerOnFocus: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: () => 'value'
|
||||
},
|
||||
displayOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
props: autoCompleteProps,
|
||||
setup(props, context) {
|
||||
return $setup({ props, context, template })
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue