Skip to content

Commit f51fb1a

Browse files
committed
useSupportCreateSuggestion doc
1 parent 30b800c commit f51fb1a

File tree

2 files changed

+338
-0
lines changed

2 files changed

+338
-0
lines changed

docs_headless/astro.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export default defineConfig({
215215
'usechoicescontext',
216216
'useinput',
217217
'usesourcecontext',
218+
'usesupportcreatesuggestion',
218219
],
219220
},
220221
{
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
---
2+
title: "useSupportCreateSuggestion"
3+
storybook_path: ra-core-controller-input-usesupportcreatesuggestion--use-support-create-suggestion
4+
---
5+
6+
This hook provides support for creating new suggestions in choice-based inputs like autocomplete components. It allows users to create new options when the desired choice doesn't exist in the available options, either through an `onCreate` callback or by rendering a creation form.
7+
8+
## Usage
9+
10+
```jsx
11+
import { useSupportCreateSuggestion } from 'ra-core';
12+
13+
const {
14+
createId,
15+
createHintId,
16+
getCreateItem,
17+
handleChange,
18+
createElement,
19+
getOptionDisabled,
20+
} = useSupportCreateSuggestion({
21+
filter: searchValue,
22+
handleChange: (eventOrValue) => {
23+
// update your input value
24+
setValue(eventOrValue?.target?.value ?? eventOrValue?.id);
25+
},
26+
onCreate: async (filter) => {
27+
// create a new option and return it
28+
return await createNewOption(filter);
29+
},
30+
});
31+
```
32+
33+
The hook accepts a configuration object and returns utilities for handling suggestion creation in choice inputs.
34+
35+
## Parameters
36+
37+
| Prop | Required | Type | Default | Description |
38+
| ----------------- | -------- | ------------------------------------------- | -------------------- | ----------------------------------------------------------------------------- |
39+
| `create` | No | `ReactElement` | - | React element rendered when users choose to create a new choice |
40+
| `createLabel` | No | `ReactNode` | `'ra.action.create'` | Label for the create choice item |
41+
| `createItemLabel` | No | `string \| ((filter: string) => ReactNode)` | - | Dynamic label that receives the filter value as parameter |
42+
| `createValue` | No | `string` | `'@@ra-create'` | Value for the create choice item |
43+
| `createHintValue` | No | `string` | `'@@ra-create-hint'` | Value for the disabled hint item |
44+
| `filter` | No | `string` | - | Current filter/search value entered by the user |
45+
| `handleChange` | Yes | `(value: any) => void` | - | Function to call when the input value changes |
46+
| `onCreate` | No | `(filter?: string) => any \| Promise<any>` | - | Function called when creating a new option (if `create` element not provided) |
47+
| `optionText` | No | `OptionText` | `'name'` | Property name for the option text |
48+
49+
## Return Value
50+
51+
The hook returns an object with:
52+
53+
- `createId`: The ID value for the create option
54+
- `createHintId`: The ID value for the create hint (disabled) option
55+
- `getCreateItem`: Function that returns the create option object
56+
- `handleChange`: Enhanced change handler that intercepts create actions
57+
- `createElement`: React element to render for creation form (null when not creating)
58+
- `getOptionDisabled`: Function to determine if an option should be disabled (i.e. if it's a hint)
59+
60+
## Example with onCreate Callback
61+
62+
```jsx
63+
import { useSupportCreateSuggestion } from 'ra-core';
64+
import { useState } from 'react';
65+
66+
const AuthorInput = () => {
67+
const [value, setValue] = useState('');
68+
const [filter, setFilter] = useState('');
69+
70+
const {
71+
getCreateItem,
72+
handleChange,
73+
getOptionDisabled,
74+
} = useSupportCreateSuggestion({
75+
filter,
76+
handleChange: (eventOrValue) => {
77+
setValue(eventOrValue?.target?.value ?? eventOrValue?.id);
78+
},
79+
onCreate: async (authorName) => {
80+
// Call your API to create a new author
81+
return await createNewAuthor(authorName);
82+
},
83+
createItemLabel: 'Create author "%{item}"',
84+
});
85+
86+
const createItem = getCreateItem(filter);
87+
const options = [
88+
...existingAuthors,
89+
createItem,
90+
];
91+
92+
return (
93+
<div>
94+
<input
95+
type="text"
96+
placeholder="Search authors..."
97+
value={filter}
98+
onChange={(e) => setFilter(e.target.value)}
99+
/>
100+
<select value={value} onChange={handleChange}>
101+
{options.map(option => (
102+
<option
103+
key={option.id}
104+
value={option.id}
105+
disabled={getOptionDisabled(option)}
106+
>
107+
{option.name}
108+
</option>
109+
))}
110+
</select>
111+
</div>
112+
);
113+
};
114+
```
115+
116+
## Example with Create Element
117+
118+
When you need more control over the creation process, you can provide a React element to be rendered for creating new options.
119+
120+
This form component can use `useCreateSuggestionContext` to access:
121+
122+
- `filter`: The search filter that triggered the creation
123+
- `onCancel`: Function to cancel the creation and hide the form
124+
- `onCreate`: Function to call when creation succeeds, passing the new item
125+
126+
```jsx
127+
import {
128+
useSupportCreateSuggestion,
129+
useCreateSuggestionContext,
130+
CreateBase
131+
} from 'ra-core';
132+
133+
const CreateAuthorForm = () => {
134+
const { filter, onCancel, onCreate } = useCreateSuggestionContext();
135+
136+
return (
137+
<CreateBase
138+
resource="authors"
139+
redirect={false}
140+
mutationOptions={{
141+
onSuccess: onCreate,
142+
}}
143+
>
144+
<SimpleForm defaultValues={{ name: filter }}>
145+
<TextInput source="name" />
146+
<TextInput source="email" />
147+
<button type="button" onClick={onCancel}>
148+
Cancel
149+
</button>
150+
</SimpleForm>
151+
</CreateBase>
152+
);
153+
};
154+
155+
const AuthorInput = () => {
156+
const [value, setValue] = useState('');
157+
const [filter, setFilter] = useState('');
158+
159+
const {
160+
getCreateItem,
161+
handleChange,
162+
createElement,
163+
getOptionDisabled,
164+
} = useSupportCreateSuggestion({
165+
filter,
166+
handleChange: (eventOrValue) => {
167+
setValue(eventOrValue?.target?.value ?? eventOrValue?.id);
168+
},
169+
create: <CreateAuthorForm />,
170+
createItemLabel: 'Create author "%{item}"',
171+
});
172+
173+
const createItem = getCreateItem(filter);
174+
const options = [
175+
...existingAuthors,
176+
createItem,
177+
];
178+
179+
return (
180+
<div>
181+
<input
182+
type="text"
183+
placeholder="Search authors..."
184+
value={filter}
185+
onChange={(e) => setFilter(e.target.value)}
186+
/>
187+
<select value={value} onChange={handleChange}>
188+
{options.map(option => (
189+
<option
190+
key={option.id}
191+
value={option.id}
192+
disabled={getOptionDisabled(option)}
193+
>
194+
{option.name}
195+
</option>
196+
))}
197+
</select>
198+
{createElement}
199+
</div>
200+
);
201+
};
202+
```
203+
204+
## `create`
205+
206+
Provides a React element that will be rendered when users choose to create a new option. When this prop is provided, the hook will render the element instead of calling `onCreate`. The element should use `useCreateSuggestionContext` to access the filter value and callback functions.
207+
208+
```jsx
209+
const CreateForm = () => {
210+
const { filter, onCancel, onCreate } = useCreateSuggestionContext();
211+
212+
const handleSubmit = async (data) => {
213+
try {
214+
const newItem = await createItem(data);
215+
onCreate(newItem); // This will select the new item and close the form
216+
} catch (error) {
217+
// Handle error
218+
}
219+
};
220+
221+
return (
222+
<form onSubmit={handleSubmit}>
223+
<input defaultValue={filter} name="name" />
224+
<button type="submit">Create</button>
225+
<button type="button" onClick={onCancel}>
226+
Cancel
227+
</button>
228+
</form>
229+
);
230+
};
231+
232+
useSupportCreateSuggestion({
233+
create: <CreateForm />
234+
});
235+
```
236+
237+
## `createLabel`
238+
239+
Sets the label for the create option. Can be a string, translation key, or any React node.
240+
241+
```jsx
242+
useSupportCreateSuggestion({
243+
createLabel: 'Add new item'
244+
});
245+
```
246+
247+
## `createItemLabel`
248+
249+
Provides a dynamic label that receives the filter value as a parameter. When provided, this creates two different behaviors:
250+
- With no filter: Shows a disabled hint option (using the `createLabel` text)
251+
- With filter: Shows an active create option with the filter value (using `createItemLabel`)
252+
253+
This provides better UX by guiding users on how to create new options.
254+
255+
```jsx
256+
useSupportCreateSuggestion({
257+
createItemLabel: 'Create category "%{item}"',
258+
// When filter is "Sports", shows: "Create category 'Sports'"
259+
});
260+
261+
// Or as a function:
262+
useSupportCreateSuggestion({
263+
createItemLabel: (filter) => `Add "${filter}" as new category`
264+
});
265+
```
266+
267+
## `createValue`
268+
269+
Customizes the value used internally to identify the create option. This is useful if the default value conflicts with your data.
270+
271+
```jsx
272+
useSupportCreateSuggestion({
273+
createValue: '@@my-create-id'
274+
});
275+
```
276+
277+
## `createHintValue`
278+
279+
Customizes the value used for the disabled hint option when `createItemLabel` is provided and no filter is set.
280+
281+
```jsx
282+
useSupportCreateSuggestion({
283+
createHintValue: '@@my-hint-id'
284+
});
285+
```
286+
287+
## `filter`
288+
289+
The current search/filter value entered by the user. This value is used to populate the create option label and is passed to the `onCreate` callback or the create element context.
290+
291+
```jsx
292+
const [searchValue, setSearchValue] = useState('');
293+
294+
useSupportCreateSuggestion({
295+
filter: searchValue
296+
});
297+
```
298+
299+
## `handleChange`
300+
301+
The function to call when the input value changes. The hook will intercept changes that match the create value and handle them specially, otherwise it will call this function with the original event or value.
302+
303+
```jsx
304+
useSupportCreateSuggestion({
305+
handleChange: (eventOrValue) => {
306+
setValue(eventOrValue?.target?.value ?? eventOrValue?.id);
307+
}
308+
});
309+
```
310+
311+
## `onCreate`
312+
313+
A function called when creating a new option, if the `create` element is not provided. Should return the newly created item.
314+
315+
```jsx
316+
useSupportCreateSuggestion({
317+
onCreate: async (filterValue) => {
318+
// create a new option and return it
319+
return await createNewOption(filterValue);
320+
}
321+
});
322+
```
323+
324+
## `optionText`
325+
326+
Specifies which property of the option objects contains the display text. If your options use a different property than `name`, specify it here.
327+
328+
```jsx
329+
useSupportCreateSuggestion({
330+
optionText: 'title', // Uses 'title' instead of 'name'
331+
});
332+
333+
// Also accepts function for more complex scenarios
334+
useSupportCreateSuggestion({
335+
optionText: (option) => `${option.firstName} ${option.lastName}`
336+
});
337+
```

0 commit comments

Comments
 (0)