Skip to content

Commit

Permalink
feat(pro:search): select field supports caching selected data (#1814)
Browse files Browse the repository at this point in the history
  • Loading branch information
sallerli1 committed Jan 16, 2024
1 parent 31266d6 commit 6e80276
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 40 deletions.
11 changes: 10 additions & 1 deletion packages/pro/search/src/ProSearch.tsx
Expand Up @@ -19,6 +19,7 @@ import SearchItemComp from './components/SearchItem'
import QuickSelectPanel from './components/quickSelect/QuickSelectPanel'
import NameSelectSegment from './components/segment/TempSegment'
import { useActiveSegment } from './composables/useActiveSegment'
import { useCacheData } from './composables/useCacheData'
import { useCommonOverlayProps } from './composables/useCommonOverlayProps'
import { useControl } from './composables/useControl'
import { useElementWidthMeasure } from './composables/useElementWidthMeasure'
Expand Down Expand Up @@ -69,7 +70,9 @@ export default defineComponent({
locale.search,
)
const { fieldKeyMap } = resolvedSearchFieldsContext
const searchStateContext = useSearchStates(props, fieldKeyMap, searchValueContext)

const { getCacheData, setCacheData, clearCacheData } = useCacheData()
const searchStateContext = useSearchStates(props, fieldKeyMap, searchValueContext, getCacheData, setCacheData)
const { searchStates, initSearchStates, clearSearchState, updateSearchValues, isSegmentVisible } =
searchStateContext

Expand Down Expand Up @@ -113,6 +116,9 @@ export default defineComponent({
},
{ immediate: true, deep: true },
)
watch([searchStates, () => searchStates.value.length], ([states]) => {
clearCacheData(states)
})

const placeholder = computed(() => props.placeholder ?? locale.search.placeholder)
const clearable = computed(() => props.clearable ?? config.clearable)
Expand Down Expand Up @@ -170,6 +176,9 @@ export default defineComponent({
mergedPrefixCls,
enableQuickSelect,
commonOverlayProps,
getCacheData,
setCacheData,
clearCacheData,

...focusStateContext,
...searchStateContext,
Expand Down
Expand Up @@ -30,7 +30,10 @@ export default defineComponent({
setActiveSegment,
setTempActive,
setOverlayOpened,
updateSegmentValue,
validateSearchState,
getCacheData,
setCacheData,
} = inject(proSearchContext)!

const searchStatesKeys = computed(() => new Set(searchStates.value?.map(state => state.fieldKey)))
Expand Down Expand Up @@ -78,24 +81,27 @@ export default defineComponent({
props.onChange?.(selectedFieldKey)

const searchField = fieldKeyMap.value.get(selectedFieldKey)!
const searchState = createSearchState(selectedFieldKey)
const hasOperators = searchField.operators && searchField.operators.length > 0
const valueSegment = hasOperators ? searchField.segments[1] : searchField.segments[0]

if (!searchState) {
return
}

let searchValue: unknown
if (isObject(searchField.keywordFallback) && searchField.keywordFallback.parse) {
searchValue = searchField.keywordFallback.parse(props.searchValue)
} else {
const hasOperators = searchField.operators && searchField.operators.length > 0
const valueSegment = hasOperators ? searchField.segments[1] : searchField.segments[0]

searchValue = valueSegment.parse(props.searchValue, [])
searchValue = valueSegment.parse(
props.searchValue,
[],
dataKey => getCacheData(searchState.key, valueSegment.name, dataKey),
(dataKey, data) => setCacheData(searchState.key, valueSegment.name, dataKey, data),
)
}

const searchState = createSearchState(selectedFieldKey, {
value: searchValue,
})

if (!searchState) {
return
}
updateSegmentValue(searchState.key, valueSegment.name, searchValue)

const segmentStates = searchState.segmentStates

Expand Down
21 changes: 21 additions & 0 deletions packages/pro/search/src/components/quickSelect/QuickSelectItem.tsx
Expand Up @@ -5,6 +5,8 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

/* eslint-disable @typescript-eslint/no-explicit-any */

import type { SearchState } from '../../composables/useSearchStates'

import { computed, defineComponent, inject, normalizeClass, ref, watch } from 'vue'
Expand All @@ -30,6 +32,8 @@ export default defineComponent({
removeSearchState,
updateSegmentValue,
updateSearchValues,
getCacheData,
setCacheData,
tempSegmentInputRef,
} = inject(proSearchContext)!

Expand Down Expand Up @@ -124,6 +128,21 @@ export default defineComponent({
return () => {
const prefixCls = `${mergedPrefixCls.value}-quick-select-item`
const classes = normalizeClass([prefixCls, ...(searchDataSegment.value?.containerClassName ?? [])])

const _getCacheData = (dataKey: string) => {
if (!searchState.value || !searchDataSegment.value) {
return
}

return getCacheData(searchState.value.key, searchDataSegment.value.name, dataKey)
}
const _setCacheData = (dataKey: string, data: any) => {
if (!searchState.value || !searchDataSegment.value) {
return
}

return setCacheData(searchState.value.key, searchDataSegment.value.name, dataKey, data)
}
return (
<div class={classes}>
<div class={`${prefixCls}-header`}>
Expand Down Expand Up @@ -159,6 +178,8 @@ export default defineComponent({
cancel,
setValue,
setOnKeyDown,
getCacheData: _getCacheData,
setCacheData: _setCacheData,
})}
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions packages/pro/search/src/components/segment/Segment.tsx
Expand Up @@ -42,6 +42,8 @@ export default defineComponent({
searchStates,
overlayOpened: _overlayOpened,
setOverlayOpened,
getCacheData,
setCacheData,
} = context
const overlayRef = ref<ɵOverlayInstance>()
const segmentInputRef = ref<SegmentInputInstance>()
Expand Down Expand Up @@ -188,6 +190,8 @@ export default defineComponent({
ok: handleConfirm,
setValue: handleChange,
setOnKeyDown: setPanelOnKeyDown,
getCacheData: dataKey => getCacheData(props.itemKey, props.segment.name, dataKey),
setCacheData: (dataKey, data) => setCacheData(props.itemKey, props.segment.name, dataKey, data),
})

contentNodeEmpty.value = !contentNode
Expand Down
70 changes: 70 additions & 0 deletions packages/pro/search/src/composables/useCacheData.ts
@@ -0,0 +1,70 @@
/**
* @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
*/

/* eslint-disable @typescript-eslint/no-explicit-any */

import type { SearchState } from './useSearchStates'

import { onUnmounted } from 'vue'

export interface CacheDataContext {
setCacheData: (itemKey: string, segmentName: string, dataKey: string, data: any) => void
getCacheData: (itemKey: string, segmentName: string, dataKey: string) => void
clearCacheData: (searchStates?: SearchState[]) => void
}

export function useCacheData(): CacheDataContext {
const cacheDataMap = new Map<string, Map<string, Record<string, any>>>()

const setCacheData = (itemKey: string, segmentName: string, dataKey: string, data: any) => {
let dataMap = cacheDataMap.get(itemKey)

if (!dataMap) {
dataMap = new Map<string, Record<string, any>>()
cacheDataMap.set(itemKey, dataMap)
}

let segmentData = dataMap.get(segmentName)

if (!segmentData) {
segmentData = {
[dataKey]: data,
}

dataMap.set(segmentName, segmentData)
} else {
segmentData[dataKey] = data
}
}

const getCacheData = (itemKey: string, segmentName: string, dataKey: string) => {
return cacheDataMap.get(itemKey)?.get(segmentName)?.[dataKey]
}

const clearCacheData = (searchStates?: SearchState[]) => {
if (!searchStates) {
cacheDataMap.clear()
return
}

const stateKeys = new Set(searchStates.map(state => state.key))

for (const key of cacheDataMap.keys()) {
if (!stateKeys.has(key)) {
cacheDataMap.delete(key)
}
}
}

onUnmounted(clearCacheData)

return {
setCacheData,
getCacheData,
clearCacheData,
}
}
Expand Up @@ -68,7 +68,7 @@ function createSearchItemContentSegments(
) {
if (searchField.type === 'multiSegment') {
return searchField.fieldConfig.segments.map(segmentConfig =>
createCustomSegment(prefixCls, dateConfig, segmentConfig),
createCustomSegment(prefixCls, dateConfig, segmentConfig, locale),
)
}

Expand All @@ -87,7 +87,7 @@ function createSearchItemContentSegments(
case 'dateRangePicker':
return createDateRangePickerSegment(prefixCls, searchField.fieldConfig, dateConfig)
case 'custom':
return createCustomSegment(prefixCls, dateConfig, searchField.fieldConfig)
return createCustomSegment(prefixCls, dateConfig, searchField.fieldConfig, locale)
default:
return
}
Expand Down
33 changes: 23 additions & 10 deletions packages/pro/search/src/composables/useSearchStates.ts
Expand Up @@ -5,6 +5,7 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { CacheDataContext } from './useCacheData'
import type { SearchValueContext } from './useSearchValues'
import type { ProSearchProps, ResolvedSearchField, SearchValue, Segment, SegmentState } from '../types'

Expand All @@ -26,10 +27,10 @@ export interface SearchState {
}

interface InitSearchState {
(key: VKey): void
(key: VKey, force: boolean): void
(key: VKey, segmentName: string): void
(key: VKey, segmentName: string, force: boolean): void
(key: string): void
(key: string, force: boolean): void
(key: string, segmentName: string): void
(key: string, segmentName: string, force: boolean): void
}

export interface SearchStateContext {
Expand All @@ -54,6 +55,8 @@ export function useSearchStates(
props: ProSearchProps,
fieldKeyMap: ComputedRef<Map<VKey, ResolvedSearchField>>,
searchValueContext: SearchValueContext,
getCacheData: CacheDataContext['getCacheData'],
setCacheData: CacheDataContext['setCacheData'],
): SearchStateContext {
const { searchValues, setSearchValues } = searchValueContext
const getKey = createStateKeyGetter()
Expand Down Expand Up @@ -124,7 +127,12 @@ export function useSearchStates(
}

const segmentState = searchState.segmentStates.find(state => state.name === name)
const input = segment.format(value, searchState.segmentStates)
const input = segment.format(
value,
searchState.segmentStates,
dataKey => getCacheData(searchState.key, name, dataKey),
(dataKey, data) => setCacheData(searchState.key, name, dataKey, data),
)

if (!segmentState) {
searchState.segmentStates.push({ name, input, value })
Expand All @@ -142,7 +150,12 @@ export function useSearchStates(
}

const segmentState = searchState.segmentStates.find(state => state.name === name)
const value = segment.parse(input, searchState.segmentStates)
const value = segment.parse(
input,
searchState.segmentStates,
dataKey => getCacheData(searchState.key, name, dataKey),
(dataKey, data) => setCacheData(searchState.key, name, dataKey, data),
)

if (segmentState) {
segmentState.value = value
Expand Down Expand Up @@ -283,7 +296,7 @@ export function useSearchStates(
let key = _getKey(fieldKey)
let hasCreatedState = false

const segmentStates = generateSegmentStates(searchField, searchValue)
const segmentStates = generateSegmentStates(searchField, searchValue, key, getCacheData, setCacheData)
const searchState = { fieldKey, searchValue, segmentStates } as SearchState

if (!searchField.multiple) {
Expand Down Expand Up @@ -331,7 +344,7 @@ export function useSearchStates(
searchStates.value = newStates
}

const initSearchState: InitSearchState = (key: VKey, segmentNameOrForce?: string | boolean, force?: boolean) => {
const initSearchState: InitSearchState = (key: string, segmentNameOrForce?: string | boolean, force?: boolean) => {
const _force = isString(segmentNameOrForce) ? force : segmentNameOrForce
const _segmentName = isString(segmentNameOrForce) ? segmentNameOrForce : undefined

Expand All @@ -342,7 +355,7 @@ export function useSearchStates(
}

const searchValue = searchState.searchValue
const segmentStates = generateSegmentStates(searchField, searchValue)
const segmentStates = generateSegmentStates(searchField, searchValue, key, getCacheData, setCacheData)

if (!_segmentName) {
searchState.segmentStates = segmentStates
Expand All @@ -369,7 +382,7 @@ export function useSearchStates(
name: searchField.label,
index: searchStates.value.length,
fieldKey: fieldKey,
segmentStates: generateSegmentStates(searchField, searchValue),
segmentStates: generateSegmentStates(searchField, searchValue, newKey, getCacheData, setCacheData),
}
searchStates.value.push(newSearchState)
mark(newKey, 'created')
Expand Down

0 comments on commit 6e80276

Please sign in to comment.