11import * as fuzzy from 'fuzzy' ;
2- import get from 'lodash/get' ;
32import uniqBy from 'lodash/uniqBy' ;
43import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
54
@@ -15,29 +14,22 @@ import Pill from '@staticcms/core/components/common/pill/Pill';
1514import CircularProgress from '@staticcms/core/components/common/progress/CircularProgress' ;
1615import classNames from '@staticcms/core/lib/util/classNames.util' ;
1716import { getFields } from '@staticcms/core/lib/util/collection.util' ;
18- import { isNullish } from '@staticcms/core/lib/util/null.util' ;
1917import { fileSearch , sortByScore } from '@staticcms/core/lib/util/search.util' ;
20- import { isEmpty } from '@staticcms/core/lib/util/string.util' ;
2118import { generateClassNames } from '@staticcms/core/lib/util/theming.util' ;
22- import {
23- addFileTemplateFields ,
24- compileStringTemplate ,
25- expandPath ,
26- extractTemplateVars ,
27- } from '@staticcms/core/lib/widgets/stringTemplate' ;
2819import { selectCollection } from '@staticcms/core/reducers/selectors/collections' ;
2920import { useAppSelector } from '@staticcms/core/store/hooks' ;
21+ import { getSelectedValue , parseHitOptions } from './util' ;
3022
3123import type {
3224 ConfigWithDefaults ,
3325 Entry ,
34- EntryData ,
3526 ObjectValue ,
3627 RelationField ,
3728 WidgetControlProps ,
3829} from '@staticcms/core' ;
3930import type { FC , ReactNode } from 'react' ;
4031import type { ListChildComponentProps } from 'react-window' ;
32+ import type { HitOption } from './types' ;
4133
4234import './RelationControl.css' ;
4335
@@ -55,64 +47,15 @@ function Option({ index, style, data }: ListChildComponentProps<{ options: React
5547 return < div style = { style } > { data . options [ index ] } </ div > ;
5648}
5749
58- export interface HitOption {
59- data : EntryData ;
60- value : string ;
61- label : string ;
62- }
63-
6450export interface Option {
6551 value : string ;
6652 label : string ;
6753}
6854
69- function getSelectedOptions ( value : HitOption [ ] | undefined | null ) : HitOption [ ] | null ;
70- function getSelectedOptions ( value : string [ ] | undefined | null ) : string [ ] | null ;
71- function getSelectedOptions ( value : string [ ] | HitOption [ ] | undefined | null ) {
72- if ( ! value || ! Array . isArray ( value ) ) {
73- return null ;
74- }
75-
76- return value ;
77- }
78-
7955function uniqOptions ( initial : HitOption [ ] , current : HitOption [ ] ) : HitOption [ ] {
8056 return uniqBy ( initial . concat ( current ) , o => o . value ) ;
8157}
8258
83- function getSelectedValue ( value : string , options : HitOption [ ] , isMultiple : boolean ) : string | null ;
84- function getSelectedValue (
85- value : string [ ] ,
86- options : HitOption [ ] ,
87- isMultiple : boolean ,
88- ) : string [ ] | null ;
89- function getSelectedValue (
90- value : string | string [ ] | null | undefined ,
91- options : HitOption [ ] ,
92- isMultiple : boolean ,
93- ) : string | string [ ] | null ;
94- function getSelectedValue (
95- value : string | string [ ] | null | undefined ,
96- options : HitOption [ ] ,
97- isMultiple : boolean ,
98- ) : string | string [ ] | null {
99- if ( isMultiple && Array . isArray ( value ) ) {
100- const selectedOptions = getSelectedOptions ( value ) ;
101- if ( selectedOptions === null ) {
102- return null ;
103- }
104-
105- const selected = selectedOptions
106- . map ( i => options . find ( o => o . value === i ) )
107- . filter ( Boolean )
108- . map ( option => ( typeof option === 'string' ? option : option ?. value ) ) as string [ ] ;
109-
110- return selected ;
111- } else {
112- return options . find ( option => option . value === value ) ?. value ?? null ;
113- }
114- }
115-
11659const DEFAULT_OPTIONS_LIMIT = 20 ;
11760
11861const RelationControl : FC < WidgetControlProps < string | string [ ] , RelationField > > = ( {
@@ -146,56 +89,6 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
14689 return field . multiple ?? false ;
14790 } , [ field . multiple ] ) ;
14891
149- const parseNestedFields = useCallback (
150- ( hit : Entry , field : string ) : string => {
151- const hitData =
152- locale != null && hit . i18n != null && hit . i18n [ locale ] != null
153- ? hit . i18n [ locale ] . data
154- : hit . data ;
155-
156- const templateVars = extractTemplateVars ( field ) ;
157- // return non template fields as is
158- if ( templateVars . length <= 0 ) {
159- return get ( hitData , field ) as string ;
160- }
161- const data = addFileTemplateFields ( hit . path , hitData ) ;
162- return compileStringTemplate ( field , null , hit . slug , data , searchCollectionFields ) ;
163- } ,
164- [ locale , searchCollectionFields ] ,
165- ) ;
166-
167- const parseHitOptions = useCallback (
168- ( hits : Entry [ ] ) => {
169- const valueField = field . value_field ;
170- const displayField = field . display_fields || [ field . value_field ] ;
171-
172- const options = hits . reduce ( ( acc , hit ) => {
173- const valuesPaths = expandPath ( { data : hit . data , path : valueField } ) ;
174- for ( let i = 0 ; i < valuesPaths . length ; i ++ ) {
175- const value = parseNestedFields ( hit , valuesPaths [ i ] ) as string ;
176-
177- const label = displayField
178- . map ( key => {
179- const displayPaths = expandPath ( { data : hit . data , path : key } ) ;
180- const path = displayPaths [ i ] ?? displayPaths [ 0 ] ;
181- if ( isNullish ( path ) || isEmpty ( path ) ) {
182- return value ;
183- }
184- return parseNestedFields ( hit , displayPaths [ i ] ?? displayPaths [ 0 ] ) ;
185- } )
186- . join ( ' ' ) ;
187-
188- acc . push ( { data : hit . data , value, label } ) ;
189- }
190-
191- return acc ;
192- } , [ ] as HitOption [ ] ) ;
193-
194- return options ;
195- } ,
196- [ field . display_fields , field . value_field , parseNestedFields ] ,
197- ) ;
198-
19992 const [ options , setOptions ] = useState < HitOption [ ] > ( [ ] ) ;
20093 const [ entries , setEntries ] = useState < Entry [ ] | null > ( null ) ;
20194 const loading = useMemo ( ( ) => ! entries , [ entries ] ) ;
@@ -232,15 +125,18 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
232125 ) ;
233126 }
234127
235- let options = uniqBy ( parseHitOptions ( hits ) , o => o . value ) ;
128+ let options = uniqBy (
129+ parseHitOptions ( hits , field , locale , searchCollectionFields ) ,
130+ o => o . value ,
131+ ) ;
236132
237133 if ( limit !== undefined && limit > 0 ) {
238134 options = options . slice ( 0 , limit ) ;
239135 }
240136
241137 setOptions ( options ) ;
242138 } ,
243- [ entries , field . file , field . options_length , field . search_fields , parseHitOptions ] ,
139+ [ entries , field , locale , searchCollectionFields ] ,
244140 ) ;
245141
246142 useEffect ( ( ) => {
@@ -261,7 +157,7 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
261157 if ( alive ) {
262158 setEntries ( options ) ;
263159
264- const hitOptions = parseHitOptions ( options ) ;
160+ const hitOptions = parseHitOptions ( options , field , locale , searchCollectionFields ) ;
265161
266162 if ( value ) {
267163 const byValue = hitOptions . reduce (
@@ -294,7 +190,7 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
294190 alive = false ;
295191 } ;
296192 // eslint-disable-next-line react-hooks/exhaustive-deps
297- } , [ searchCollection , config , loading , parseHitOptions ] ) ;
193+ } , [ searchCollection , config , loading , field , locale , searchCollectionFields ] ) ;
298194
299195 const uniqueOptions = useMemo ( ( ) => {
300196 let uOptions = uniqOptions ( initialOptions , options ) ;
0 commit comments