@inseefr/lunatic 3.8.0 → 3.8.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/components/Suggester/Suggester.js +19 -7
  2. package/components/Suggester/Suggester.js.map +1 -1
  3. package/components/Suggester/Suggester.spec.js +8 -1
  4. package/components/Suggester/Suggester.spec.js.map +1 -1
  5. package/components/Suggester/useSuggestions.d.ts +21 -2
  6. package/components/Suggester/useSuggestions.js +26 -12
  7. package/components/Suggester/useSuggestions.js.map +1 -1
  8. package/esm/components/Suggester/Suggester.js +21 -8
  9. package/esm/components/Suggester/Suggester.js.map +1 -1
  10. package/esm/components/Suggester/Suggester.spec.js +8 -1
  11. package/esm/components/Suggester/Suggester.spec.js.map +1 -1
  12. package/esm/components/Suggester/useSuggestions.d.ts +21 -2
  13. package/esm/components/Suggester/useSuggestions.js +24 -10
  14. package/esm/components/Suggester/useSuggestions.js.map +1 -1
  15. package/esm/utils/search/SearchInterface.d.ts +1 -0
  16. package/esm/utils/search/SearchMinisearch.d.ts +1 -0
  17. package/esm/utils/search/SearchMinisearch.js +6 -0
  18. package/esm/utils/search/SearchMinisearch.js.map +1 -1
  19. package/esm/utils/search/SuggestersDatabase.d.ts +7 -0
  20. package/esm/utils/search/SuggestersDatabase.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/components/Suggester/Suggester.spec.tsx +9 -1
  23. package/src/components/Suggester/Suggester.tsx +21 -8
  24. package/src/components/Suggester/useSuggestions.ts +39 -18
  25. package/src/utils/search/SearchInterface.ts +2 -0
  26. package/src/utils/search/SearchMinisearch.ts +7 -0
  27. package/src/utils/search/SuggestersDatabase.ts +10 -0
  28. package/tsconfig.build.tsbuildinfo +1 -1
  29. package/utils/search/SearchInterface.d.ts +1 -0
  30. package/utils/search/SearchMinisearch.d.ts +1 -0
  31. package/utils/search/SearchMinisearch.js +6 -0
  32. package/utils/search/SearchMinisearch.js.map +1 -1
  33. package/utils/search/SuggestersDatabase.d.ts +7 -0
  34. package/utils/search/SuggestersDatabase.js.map +1 -1
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
2
2
  import type { LunaticComponentProps } from '../type';
3
3
  import { CustomSuggester } from './CustomSuggester';
4
4
  import { getComponentErrors } from '../shared/ComponentErrors/ComponentErrors';
5
- import { OTHER_VALUE, useSuggestions } from './useSuggestions';
5
+ import { OTHER_VALUE, useStore, useSuggestions } from './useSuggestions';
6
6
  import D from '../../i18n';
7
7
  import type { SuggesterOptionType } from './SuggesterType';
8
8
 
@@ -36,6 +36,10 @@ export function WrappedSuggester({
36
36
  arbitrary,
37
37
  arbitraryValue,
38
38
  }: LunaticComponentProps<'Suggester'>) {
39
+ const { store, storeState, setStoreState, getLabelById } = useStore({
40
+ storeName,
41
+ });
42
+
39
43
  // Default options should not change between render
40
44
  // so we can break the rule of hooks here
41
45
  const computeSelectedOptions = (): [SuggesterOptionType] | [] => {
@@ -47,7 +51,7 @@ export function WrappedSuggester({
47
51
  }
48
52
  const labelResponse = optionResponses?.find((o) => o.attribute === 'label');
49
53
  if (!labelResponse) {
50
- return [{ id: value, label: value, value: value }];
54
+ return [{ id: value, label: getLabelById(value), value: value }];
51
55
  }
52
56
  const label = executeExpression(
53
57
  { value: labelResponse.name, type: 'VTL' },
@@ -73,7 +77,9 @@ export function WrappedSuggester({
73
77
 
74
78
  const { state, options, search, setSearch, onFocus, onBlur } = useSuggestions(
75
79
  {
76
- storeName: storeName,
80
+ store,
81
+ storeState,
82
+ setStoreState,
77
83
  allowArbitrary: !!arbitrary,
78
84
  selectedOptions: selectedOptions,
79
85
  }
@@ -110,7 +116,7 @@ export function WrappedSuggester({
110
116
  { name: response.name, value: null },
111
117
  ];
112
118
  // User chose an arbitrary option or clear the value
113
- if (arbitrary && arbitrary.response) {
119
+ if (arbitrary?.response) {
114
120
  newResponses.push({
115
121
  name: arbitrary.response.name,
116
122
  value: v?.id === OTHER_VALUE ? search : null,
@@ -145,11 +151,18 @@ export function WrappedSuggester({
145
151
  setSearch('');
146
152
  };
147
153
 
148
- // Fix display issue (when handleChanges is called outside this component (in management mode, return to FORCED value by example) )
149
- // We have to re-compute actual selection
150
154
  useEffect(() => {
151
- if (value) {
152
- setSelectedOptions(computeSelectedOptions());
155
+ // Fix display issue (when handleChanges is called outside this component (in management mode, return to FORCED value by example)
156
+ // "value" does'nt match selectedOption's "id"
157
+ if (value && selectedOptions[0]?.id !== value) {
158
+ const actualSelection = computeSelectedOptions();
159
+ const selectedOptionsWithLabel = [
160
+ {
161
+ ...actualSelection[0],
162
+ label: getLabelById(actualSelection[0]?.id),
163
+ },
164
+ ] as [SuggesterOptionType];
165
+ setSelectedOptions(selectedOptionsWithLabel);
153
166
  }
154
167
  // eslint-disable-next-line react-hooks/exhaustive-deps
155
168
  }, [value]);
@@ -1,11 +1,16 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import type { SuggesterOptionType } from './SuggesterType';
3
3
  import { useEffectDebounced } from '../../hooks/useDebounce';
4
- import { getSearchForStore } from '../../utils/search/SuggestersDatabase';
4
+ import {
5
+ getSearchForStore,
6
+ SearchStore,
7
+ } from '../../utils/search/SuggestersDatabase';
5
8
  import { useRefSync } from '../../hooks/useRefSync';
6
9
 
7
10
  type Props = {
8
- storeName: string;
11
+ store: SearchStore;
12
+ storeState: State;
13
+ setStoreState: (s: State) => any;
9
14
  selectedOptions: SuggesterOptionType[];
10
15
  allowArbitrary: boolean;
11
16
  };
@@ -14,20 +19,13 @@ export const OTHER_VALUE = 'OTHER';
14
19
 
15
20
  type State = 'success' | 'loading' | 'error';
16
21
 
17
- export function useSuggestions({
18
- storeName,
19
- selectedOptions,
20
- allowArbitrary,
21
- }: Props) {
22
- const [searchQuery, setSearchQuery] = useState('');
22
+ export function useStore({ storeName }: { storeName: string }) {
23
23
  const store = getSearchForStore(storeName);
24
24
  const searchIndexRef = useRefSync(store.index);
25
- // eslint-disable-next-line prefer-const
26
- let [options, setOptions] = useState(selectedOptions);
27
25
  const [state, setState] = useState<State>(
28
26
  store.error !== null
29
27
  ? 'error'
30
- : store.search.isIndexed()
28
+ : store?.search.isIndexed()
31
29
  ? 'success'
32
30
  : 'loading'
33
31
  );
@@ -47,19 +45,42 @@ export function useSuggestions({
47
45
  });
48
46
  }, [searchIndexRef]);
49
47
 
48
+ return {
49
+ store,
50
+ storeState: state,
51
+ setStoreState: setState,
52
+ getLabelById: (id: any) => {
53
+ if (!id) return '';
54
+ return store.search?.getFieldsById(id).label ?? '';
55
+ },
56
+ };
57
+ }
58
+
59
+ export function useSuggestions({
60
+ store,
61
+ storeState: state,
62
+ setStoreState: setState,
63
+ selectedOptions,
64
+ allowArbitrary,
65
+ }: Props) {
66
+ const [searchQuery, setSearchQuery] = useState('');
67
+ // eslint-disable-next-line prefer-const
68
+ let [options, setOptions] = useState(selectedOptions);
69
+
50
70
  useEffectDebounced(
51
71
  () => {
52
72
  // Do not reset search for empty search
53
73
  if (!searchQuery) {
54
74
  return;
55
75
  }
56
- store.search
57
- ?.search(searchQuery)
58
- .then((r) => {
59
- setOptions(r);
60
- setState('success');
61
- })
62
- .catch(() => setState('error'));
76
+ if (store.error === null)
77
+ store.search
78
+ ?.search(searchQuery)
79
+ .then((r) => {
80
+ setOptions(r);
81
+ setState('success');
82
+ })
83
+ .catch(() => setState('error'));
63
84
  },
64
85
  [searchQuery],
65
86
  300
@@ -14,4 +14,6 @@ export interface SearchInterface<T extends IndexEntry> {
14
14
  index(data: T[]): Promise<void>;
15
15
 
16
16
  search(q: string): Promise<T[]>;
17
+
18
+ getFieldsById(id: any): T;
17
19
  }
@@ -51,4 +51,11 @@ export class SearchMinisearch<T extends IndexEntry>
51
51
 
52
52
  return data;
53
53
  }
54
+
55
+ getFieldsById(id: any) {
56
+ if (!this.db) {
57
+ return {} as T;
58
+ }
59
+ return this.db.getStoredFields(id) as T;
60
+ }
54
61
  }
@@ -23,6 +23,16 @@ export function registerSuggesters(
23
23
  }
24
24
  }
25
25
 
26
+ export type SearchStore =
27
+ | {
28
+ error: string;
29
+ }
30
+ | {
31
+ error?: null;
32
+ search: SearchInterface<IndexEntry>;
33
+ index: () => Promise<void>;
34
+ };
35
+
26
36
  export function getSearchForStore(storeName: string) {
27
37
  const search = suggesters.get(storeName);
28
38
  if (!search) {