-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
/
Copy pathuseSupportCreateSuggestion.tsx
124 lines (114 loc) · 5.01 KB
/
useSupportCreateSuggestion.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import * as React from 'react';
import {
ChangeEvent,
createContext,
isValidElement,
ReactElement,
useContext,
useState,
} from 'react';
import { Identifier, useTranslate } from 'ra-core';
/**
* This hook provides support for suggestion creation in inputs which have choices.
*
* @param options The hook option
* @param {ReactElement} options.create A react element which will be rendered when users choose to create a new choice. This component must call the `useCreateSuggestionContext` hook which provides `onCancel`, `onCreate` and `filter`. See the examples.
* @param {String} options.createLabel Optional. The label for the choice item allowing users to create a new choice. Can be a translation key. Defaults to `ra.action.create`.
* @param {String} options.createItemLabel Optional. The label for the choice item allowing users to create a new choice when they already entered a filter. Can be a translation key. The translation will receive an `item` parameter. Defaults to `ra.action.create_item`.
* @param {any} options.createValue Optional. The value for the choice item allowing users to create a new choice. Defaults to `@@ra-create`.
* @param {String} options.filter Optional. The filter users may have already entered. Useful for autocomplete inputs for example.
* @param {OnCreateHandler} options.onCreate Optional. A function which will be called when users choose to create a new choice, if the `create` option wasn't provided.
* @param handleChange: a function to pass to the input. Receives the same parameter as the original event handler and an additional newItem parameter if a new item was create.
* @returns {UseSupportCreateValue} An object with the following properties:
* - getCreateItem: a function which will return the label of the choice for create a new choice.
* - createElement: a React element to render after the input. It will be rendered when users choose to create a new choice. It renders null otherwise.
*/
export const useSupportCreateSuggestion = (
options: SupportCreateSuggestionOptions
): UseSupportCreateValue => {
const {
create,
createLabel = 'ra.action.create',
createItemLabel = 'ra.action.create_item',
createValue = '@@ra-create',
filter,
handleChange,
onCreate,
} = options;
const translate = useTranslate();
const [renderOnCreate, setRenderOnCreate] = useState(false);
const context = {
filter,
onCancel: () => setRenderOnCreate(false),
onCreate: item => {
setRenderOnCreate(false);
handleChange(undefined, item);
},
};
return {
getCreateItem: () => {
return {
id: createValue,
name:
filter && createItemLabel
? translate(createItemLabel, {
item: filter,
_: createItemLabel,
})
: translate(createLabel, { _: createLabel }),
};
},
handleChange: async eventOrValue => {
const value = eventOrValue.target?.value || eventOrValue;
const finalValue = Array.isArray(value) ? [...value].pop() : value;
if (eventOrValue?.preventDefault) {
eventOrValue.preventDefault();
eventOrValue.stopPropagation();
}
if (finalValue?.id === createValue || finalValue === createValue) {
if (!isValidElement(create)) {
const newSuggestion = await onCreate(filter);
if (newSuggestion) {
handleChange(eventOrValue, newSuggestion);
return;
}
} else {
setRenderOnCreate(true);
return;
}
}
handleChange(eventOrValue, undefined);
},
createElement:
renderOnCreate && isValidElement(create) ? (
<CreateSuggestionContext.Provider value={context}>
{create}
</CreateSuggestionContext.Provider>
) : null,
};
};
export interface SupportCreateSuggestionOptions {
create?: ReactElement;
createValue?: string;
createLabel?: string;
createItemLabel?: string;
filter?: string;
handleChange: (value: any, newChoice: any) => void;
onCreate?: OnCreateHandler;
}
export interface UseSupportCreateValue {
getCreateItem: () => { id: Identifier; name: string };
handleChange: (eventOrValue: ChangeEvent | any) => Promise<void>;
createElement: ReactElement | null;
}
const CreateSuggestionContext = createContext<CreateSuggestionContextValue>(
undefined
);
interface CreateSuggestionContextValue {
filter?: string;
onCreate: (choice: any) => void;
onCancel: () => void;
}
export const useCreateSuggestionContext = () =>
useContext(CreateSuggestionContext);
export type OnCreateHandler = (filter?: string) => any | Promise<any>;