Skip to content

Commit

Permalink
feat(pro:search): search btn triggers search change now (#1321)
Browse files Browse the repository at this point in the history
  • Loading branch information
sallerli1 committed Dec 15, 2022
1 parent 61a51fd commit e44673f
Show file tree
Hide file tree
Showing 16 changed files with 536 additions and 380 deletions.
31 changes: 26 additions & 5 deletions packages/pro/search/src/ProSearch.tsx
Expand Up @@ -18,9 +18,11 @@ import { useCommonOverlayProps } from './composables/useCommonOverlayProps'
import { useFocusedState } from './composables/useFocusedState'
import { useSearchItems } from './composables/useSearchItem'
import { useSearchItemErrors } from './composables/useSearchItemErrors'
import { useSearchStates } from './composables/useSearchStates'
import { tempSearchStateKey, useSearchStates } from './composables/useSearchStates'
import { useSearchTrigger } from './composables/useSearchTrigger'
import { useSearchValues } from './composables/useSearchValues'
import SearchItemComp from './searchItem/SearchItem'
import SearchItemTagComp from './searchItem/SearchItemTag'
import { proSearchContext } from './token'
import { type SearchItem, proSearchProps } from './types'
import { renderIcon } from './utils/RenderIcon'
Expand All @@ -47,6 +49,7 @@ export default defineComponent({
errors,
dateConfig,
)
const searchTriggerContext = useSearchTrigger()
const elementRef = ref<HTMLElement | undefined>()

const activeSegmentContext = useActiveSegment(
Expand All @@ -66,7 +69,7 @@ export default defineComponent({

const currentZIndex = useZIndex(toRef(props, 'zIndex'), toRef(componentCommon, 'overlayZIndex'), focused)

const { initSearchStates, clearSearchState } = searchStateContext
const { initSearchStates, clearSearchState, getSearchStateByKey } = searchStateContext
const { activeSegment } = activeSegmentContext

watch(
Expand Down Expand Up @@ -98,8 +101,13 @@ export default defineComponent({

expose({ focus, blur })

const handleSearchBtnClick = () => {
const { onSearchTrigger, triggerSearch } = searchTriggerContext
onSearchTrigger(() => {
callEmit(props.onSearch, searchValues.value)
}, 'post')

const handleSearchBtnClick = () => {
triggerSearch()
}
const handleClearBtnClick = () => {
clearSearchState()
Expand All @@ -120,13 +128,26 @@ export default defineComponent({

...searchStateContext,
...activeSegmentContext,
...searchTriggerContext,
})

return () => {
const prefixCls = mergedPrefixCls.value

const overflowSlots = {
item: (item: SearchItem) => <SearchItemComp key={item.key} tagOnly={true} searchItem={item} />,
item: (item: SearchItem) => {
const searchState = getSearchStateByKey(item.key)!

const tagSegments = item.segments.map(segment => {
const segmentValue = searchState.segmentValues.find(sv => sv.name === segment.name)!
return {
name: segment.name,
input: segment.format(segmentValue?.value),
}
})

return <SearchItemTagComp key={item.key} itemKey={item.key} segments={tagSegments} error={item.error} />
},
rest: (rest: SearchItem[]) => (
<span class={`${prefixCls}-search-item ${prefixCls}-search-item-tag`}>
{slots.overflowedLabel?.(rest) ?? `+ ${rest.length}`}
Expand All @@ -142,7 +163,7 @@ export default defineComponent({
v-show={!focused.value}
v-slots={overflowSlots}
prefixCls={prefixCls}
dataSource={searchItems.value}
dataSource={searchItems.value.filter(item => item.key !== tempSearchStateKey)}
getKey={item => item.key}
maxLabel={props.maxLabel}
/>
Expand Down
46 changes: 46 additions & 0 deletions packages/pro/search/src/composables/useSearchTrigger.ts
@@ -0,0 +1,46 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

export interface SearchTriggerContext {
triggerSearch: () => Promise<void>
onSearchTrigger: (cb: Handler, enforce?: HandlerEnforce) => void
}

type HandlerEnforce = 'pre' | 'post'
type Handler = () => void | Promise<void>

export function useSearchTrigger(): SearchTriggerContext {
const preHandlers: Handler[] = []
const handlers: Handler[] = []
const postHandlers: Handler[] = []

const onSearchTrigger = (cb: Handler, enforce?: HandlerEnforce) => {
if (enforce === 'pre') {
preHandlers.unshift(cb)
} else if (enforce === 'post') {
postHandlers.push(cb)
} else {
handlers.push(cb)
}
}

const _invokeHandlers = async (handlers: Handler[]) => {
for (const handler of handlers) {
await handler()
}
}
const triggerSearch = async () => {
await _invokeHandlers(preHandlers)
await _invokeHandlers(handlers)
await _invokeHandlers(postHandlers)
}

return {
triggerSearch,
onSearchTrigger,
}
}
14 changes: 14 additions & 0 deletions packages/pro/search/src/composables/useSegmentStates.ts
Expand Up @@ -37,8 +37,10 @@ export function useSegmentStates(
removeSearchState,
convertStateToValue,
initTempSearchState,
activeSegment,
changeActive,
setInactive,
onSearchTrigger,
} = proSearchContext
const segmentStates = ref<SegmentStates>({})

Expand Down Expand Up @@ -150,6 +152,18 @@ export function useSegmentStates(
}
}

onSearchTrigger(() => {
if (
!props.searchItem?.key ||
!activeSegment.value?.itemKey ||
activeSegment.value.itemKey !== props.searchItem.key
) {
return
}

handleSegmentConfirm(activeSegment.value.name, true)
}, 'pre')

return {
segmentStates,
initSegmentStates,
Expand Down
110 changes: 15 additions & 95 deletions packages/pro/search/src/searchItem/SearchItem.tsx
Expand Up @@ -7,31 +7,21 @@

import { computed, defineComponent, inject, normalizeClass, provide, watch } from 'vue'

import { IxTooltip } from '@idux/components/tooltip'

import { tempSearchStateKey } from '../composables/useSearchStates'
import { useSegmentOverlayUpdate } from '../composables/useSegmentOverlayUpdate'
import { useSegmentStates } from '../composables/useSegmentStates'
import { proSearchContext, searchItemContext } from '../token'
import { searchItemProps } from '../types'
import { renderIcon } from '../utils/RenderIcon'
import SearchItemTag from './SearchItemTag'
import Segment from './Segment'

export default defineComponent({
props: searchItemProps,
setup(props) {
const context = inject(proSearchContext)!
const {
props: proSearchProps,
mergedPrefixCls,
activeSegment,
changeActive,
setActiveSegment,
removeSearchState,
} = context
const { props: proSearchProps, mergedPrefixCls, activeSegment } = context

const prefixCls = computed(() => `${mergedPrefixCls.value}-search-item`)

const segmentStateContext = useSegmentStates(props, proSearchProps, context)
const segmentOverlayUpdateContext = useSegmentOverlayUpdate()
const { segmentStates, initSegmentStates } = segmentStateContext
Expand Down Expand Up @@ -69,101 +59,31 @@ export default defineComponent({
})
})

const setSegmentActive = (name: string) => {
setActiveSegment({
itemKey: props.searchItem!.key,
name,
overlayOpened: true,
})
}
const handleCloseIconClick = (evt: Event) => {
evt.stopPropagation()
removeSearchState(props.searchItem!.key)
}

const handleTagSegmentMouseDown = (evt: Event, name: string) => {
if (proSearchProps.disabled) {
return
}

setSegmentActive(name)

if (name === 'name') {
changeActive(1)
}
}

const renderTag = () => {
const content = segmentRenderDatas.value.map(data => data.input).join(' ')

return [
<span class={`${prefixCls.value}-tag-segments`} title={content}>
{segmentRenderDatas.value.map(data => (
<span
class={`${prefixCls.value}-tag-segment`}
onMousedown={evt => handleTagSegmentMouseDown(evt, data.name)}
>
{data.input}
</span>
))}
</span>,
<span class={`${prefixCls.value}-tag-content`} title={content}>
{content}
</span>,
]
}

return () => {
const children = []
if (!isActive.value || proSearchProps.disabled) {
return props.searchItem?.key !== tempSearchStateKey ? (
<SearchItemTag
itemKey={props.searchItem!.key}
segments={segmentRenderDatas.value}
error={props.searchItem!.error}
/>
) : undefined
}

if (!props.tagOnly) {
children.push(
...segmentRenderDatas.value.map(segment => (
return (
<span class={classes.value} onMousedown={evt => evt.preventDefault()}>
{segmentRenderDatas.value.map(segment => (
<Segment
v-show={isActive.value}
key={segment.name}
itemKey={props.searchItem!.key}
input={segment.input}
value={segment.value}
disabled={segment.name === 'name' && props.searchItem!.key !== tempSearchStateKey}
segment={segment}
/>
)),
)
}

if (!isActive.value) {
children.push(...renderTag())

if (!proSearchProps.disabled) {
children.push(
<span class={`${prefixCls.value}-close-icon`} onClick={handleCloseIconClick}>
{renderIcon('close')}
</span>,
)
}
}

const itemNode = (
<span
class={classes.value}
v-show={(isActive.value && !proSearchProps.disabled) || props.searchItem?.key !== tempSearchStateKey}
onMousedown={evt => evt.preventDefault()}
>
{children}
))}
</span>
)

const message = props.searchItem?.error?.message
if (isActive.value || !message) {
return itemNode
}

return (
<IxTooltip class={`${prefixCls.value}-invalid-tooltip`} title={message} placement="topStart" offset={[0, 15]}>
{itemNode}
</IxTooltip>
)
}
},
})
88 changes: 88 additions & 0 deletions packages/pro/search/src/searchItem/SearchItemTag.tsx
@@ -0,0 +1,88 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { computed, defineComponent, inject, normalizeClass } from 'vue'

import { IxTooltip } from '@idux/components/tooltip'

import { proSearchContext } from '../token'
import { searchItemTagProps } from '../types'
import { renderIcon } from '../utils/RenderIcon'
export default defineComponent({
props: searchItemTagProps,
setup(props) {
const context = inject(proSearchContext)!
const { props: proSearchProps, mergedPrefixCls, changeActive, setActiveSegment, removeSearchState } = context

const prefixCls = computed(() => `${mergedPrefixCls.value}-search-item-tag`)
const classes = computed(() => {
return normalizeClass({
[prefixCls.value]: true,
[`${prefixCls.value}-invalid`]: !!props?.error,
})
})

const setSegmentActive = (name: string) => {
setActiveSegment({
itemKey: props.itemKey!,
name,
overlayOpened: true,
})
}
const handleCloseIconClick = (evt: Event) => {
evt.stopPropagation()
removeSearchState(props.itemKey!)
}

const handleTagSegmentMouseDown = (name: string) => {
if (proSearchProps.disabled) {
return
}

setSegmentActive(name)

if (name === 'name') {
changeActive(1)
}
}

const renderTag = () => {
const content = props.segments!.map(data => data.input).join(' ')

return [
<span class={`${prefixCls.value}-segments`} title={content}>
{props.segments!.map(segmeng => (
<span class={`${prefixCls.value}-segment`} onMousedown={() => handleTagSegmentMouseDown(segmeng.name)}>
{segmeng.input}
</span>
))}
</span>,
<span class={`${prefixCls.value}-content`} title={content}>
{content}
</span>,
]
}

return () => (
<IxTooltip
class={`${prefixCls.value}-invalid-tooltip`}
title={props.error?.message}
placement="topStart"
offset={[0, 15]}
>
<span class={classes.value} onMousedown={evt => evt.preventDefault()}>
{renderTag()}
{!proSearchProps.disabled && (
<span class={`${prefixCls.value}-close-icon`} onClick={handleCloseIconClick}>
{renderIcon('close')}
</span>
)}
</span>
</IxTooltip>
)
},
})

0 comments on commit e44673f

Please sign in to comment.