styles(autocomplete): [autocomplete] add autocomplete types (#1322)

This commit is contained in:
jxhhdx 2024-01-24 12:19:44 +08:00 committed by GitHub
parent fc10ea4cf6
commit e917e54df0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 249 additions and 83 deletions

View File

@ -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()

View File

@ -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
}

View File

@ -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>

View File

@ -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 })
}