@nolwenture/formik-jsonschema-form 0.17.0 → 0.18.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.
- package/README.md +21 -0
- package/dist/fields/SearchableSelectField.d.ts +3 -0
- package/dist/fields/SearchableSelectField.js +57 -0
- package/dist/fields/index.d.ts +1 -0
- package/dist/fields/index.js +1 -0
- package/dist/sampleData/completeSchema.js +30 -0
- package/dist/styledComponents.d.ts +24 -0
- package/dist/styledComponents.js +28 -0
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -409,6 +409,8 @@ export function MyFormComponent() {
|
|
|
409
409
|
Enum fields are fields with multiple options to either choose `oneOf` or `anyOf` from.
|
|
410
410
|
There is an alternative field implementation for "anyOf" that when given `"ui:field": "SearchMultiTagSelectField"` results in a searchable multi tag select field.
|
|
411
411
|
|
|
412
|
+
A searchable single select field is also available for `oneOf` and `enum`. To use it, add the following to the field's uiSchema: `"ui:field": "SearchableSelectField"`
|
|
413
|
+
|
|
412
414
|
The type of the field holding the enum is defined by its return value. `oneOf` enums will only return a single value and therefore, the type of the returned value is used as the field type (in the following example, "number" is the correct field type).
|
|
413
415
|
|
|
414
416
|
`anyOf`enums will return the value(s) inside an array, even if only one option was selected. Therefore, the correct field type for the field holding the `anyOf`-enum is "array".
|
|
@@ -451,6 +453,16 @@ const schema = {
|
|
|
451
453
|
{ const: 4, title: "four" },
|
|
452
454
|
],
|
|
453
455
|
},
|
|
456
|
+
enumSearchableOneOf: {
|
|
457
|
+
type: "string",
|
|
458
|
+
title: "Enum searchable select oneOf",
|
|
459
|
+
oneOf: [
|
|
460
|
+
{ const: "one", title: "One" },
|
|
461
|
+
{ const: "two", title: "Two" },
|
|
462
|
+
{ const: "three", title: "Three" },
|
|
463
|
+
{ const: "four", title: "Four" },
|
|
464
|
+
],
|
|
465
|
+
},
|
|
454
466
|
},
|
|
455
467
|
};
|
|
456
468
|
|
|
@@ -462,6 +474,15 @@ const uiSchema = {
|
|
|
462
474
|
emptySearchMessage: "...no matching results found",
|
|
463
475
|
},
|
|
464
476
|
},
|
|
477
|
+
enumSearchableOneOf: {
|
|
478
|
+
"ui:field": "SearchableSelectField",
|
|
479
|
+
"ui:options": {
|
|
480
|
+
placeholder: "Search...",
|
|
481
|
+
clearButtonText: "Clear",
|
|
482
|
+
expandText: "More results",
|
|
483
|
+
emptySearchMessage: "...no matching results found",
|
|
484
|
+
},
|
|
485
|
+
},
|
|
465
486
|
};
|
|
466
487
|
```
|
|
467
488
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef, useContext, useMemo } from "react";
|
|
3
|
+
import { useExtractUiSchema, useSetDefault } from "../hooks";
|
|
4
|
+
import { FormRegistry } from "../Form";
|
|
5
|
+
import { useField } from "formik";
|
|
6
|
+
import { getEnums } from "../utils";
|
|
7
|
+
export const SearchableSelectField = (props) => {
|
|
8
|
+
const { templates: { FieldTemplate }, components: { SearchableSelectContainer, SearchDropdownContainer, SearchListOption, SearchListExpand, SearchNotFound, SearchInput, SearchClearButton, SearchInputContainer, }, } = useContext(FormRegistry);
|
|
9
|
+
useSetDefault(props);
|
|
10
|
+
const { name, uiSchema } = props;
|
|
11
|
+
const { props: inputProps, placeholder, clearButtonText, expandText, notFoundMessage, } = useExtractUiSchema(uiSchema);
|
|
12
|
+
const [_, __, { setValue }] = useField(name);
|
|
13
|
+
const enums = useMemo(() => getEnums(props), [props]);
|
|
14
|
+
const options = (enums === null || enums === void 0 ? void 0 : enums.values) || [];
|
|
15
|
+
const [showOptions, setShowOptions] = useState(false);
|
|
16
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
17
|
+
const [displayedItems, setDisplayedItems] = useState([]);
|
|
18
|
+
const [itemsToShow, setItemsToShow] = useState(10);
|
|
19
|
+
const moreRef = useRef(null);
|
|
20
|
+
const containerRef = useRef(null);
|
|
21
|
+
const loadMoreItems = () => {
|
|
22
|
+
setItemsToShow((prev) => prev + 10);
|
|
23
|
+
};
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setDisplayedItems(options
|
|
26
|
+
.filter((option) => { var _a, _b; return ((_b = (_a = option.title) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : "").includes(searchTerm.toLowerCase()); })
|
|
27
|
+
.slice(0, itemsToShow));
|
|
28
|
+
}, [itemsToShow, options, searchTerm]);
|
|
29
|
+
const handleSearchChange = (event) => {
|
|
30
|
+
setSearchTerm(event.target.value);
|
|
31
|
+
setItemsToShow(10);
|
|
32
|
+
setShowOptions(true);
|
|
33
|
+
};
|
|
34
|
+
const handleSelect = (item) => {
|
|
35
|
+
var _a;
|
|
36
|
+
setSearchTerm((_a = item.title) !== null && _a !== void 0 ? _a : "");
|
|
37
|
+
setValue(item.const);
|
|
38
|
+
setShowOptions(false);
|
|
39
|
+
};
|
|
40
|
+
const handleFocus = () => {
|
|
41
|
+
setShowOptions(true);
|
|
42
|
+
};
|
|
43
|
+
const handleBlur = (event) => {
|
|
44
|
+
if (containerRef.current &&
|
|
45
|
+
!containerRef.current.contains(event.relatedTarget)) {
|
|
46
|
+
setShowOptions(false);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const handleClear = () => {
|
|
50
|
+
setSearchTerm("");
|
|
51
|
+
setValue(undefined);
|
|
52
|
+
};
|
|
53
|
+
const filteredOptions = options.filter((option) => { var _a, _b; return ((_b = (_a = option.title) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : "").includes(searchTerm.toLowerCase()); });
|
|
54
|
+
const showMoreOption = displayedItems.length < filteredOptions.length &&
|
|
55
|
+
displayedItems.length >= 10;
|
|
56
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsxs(SearchableSelectContainer, { ref: containerRef, onBlur: handleBlur, children: [_jsxs(SearchInputContainer, { children: [_jsx(SearchInput, Object.assign({}, inputProps, { type: "text", placeholder: placeholder, value: searchTerm, onChange: handleSearchChange, onFocus: handleFocus })), searchTerm && (_jsx(SearchClearButton, { onClick: handleClear, children: clearButtonText !== null && clearButtonText !== void 0 ? clearButtonText : "X" }))] }), showOptions && (_jsxs(SearchDropdownContainer, { role: "dropdown-list", ref: moreRef, children: [displayedItems.length > 0 ? (displayedItems.map((item, index) => (_jsx(SearchListOption, { role: "filtered-list", onClick: () => handleSelect(item), onMouseDown: (e) => e.preventDefault(), children: item.title }, index)))) : (_jsx(SearchNotFound, { onMouseDown: (e) => e.preventDefault(), children: notFoundMessage !== null && notFoundMessage !== void 0 ? notFoundMessage : "No match found" })), showMoreOption && (_jsx(SearchListExpand, { ref: moreRef, onMouseDown: (e) => e.preventDefault(), onClick: loadMoreItems, children: expandText !== null && expandText !== void 0 ? expandText : "More..." }))] }))] }) })));
|
|
57
|
+
};
|
package/dist/fields/index.d.ts
CHANGED
package/dist/fields/index.js
CHANGED
|
@@ -68,6 +68,27 @@ export const completeSchema = {
|
|
|
68
68
|
{ const: "dragon", title: "dragon" },
|
|
69
69
|
],
|
|
70
70
|
},
|
|
71
|
+
searchableSelect: {
|
|
72
|
+
type: "string",
|
|
73
|
+
title: "Enum searchable select oneOf",
|
|
74
|
+
description: "Enum searchable select oneOf description",
|
|
75
|
+
oneOf: [
|
|
76
|
+
{ const: "cat", title: "cat" },
|
|
77
|
+
{ const: "dog", title: "dog" },
|
|
78
|
+
{ const: "bunny", title: "bunny" },
|
|
79
|
+
{ const: "canary", title: "canary" },
|
|
80
|
+
{ const: "hamster", title: "hamster" },
|
|
81
|
+
{ const: "goldfish", title: "goldfish" },
|
|
82
|
+
{ const: "dragon", title: "dragon" },
|
|
83
|
+
{ const: "lizard", title: "lizard" },
|
|
84
|
+
{ const: "frog", title: "frog" },
|
|
85
|
+
{ const: "parrot", title: "parrot" },
|
|
86
|
+
{ const: "turtle", title: "turtle" },
|
|
87
|
+
{ const: "raccoon", title: "raccoon" },
|
|
88
|
+
{ const: "tiger", title: "tiger" },
|
|
89
|
+
{ const: "lion", title: "lion" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
71
92
|
null: { type: "null", title: "Null", description: "Null description" },
|
|
72
93
|
nested: {
|
|
73
94
|
type: "object",
|
|
@@ -293,5 +314,14 @@ export const completeUiSchema = {
|
|
|
293
314
|
emptySearchMessage: "...no matching results found",
|
|
294
315
|
},
|
|
295
316
|
},
|
|
317
|
+
searchableSelect: {
|
|
318
|
+
"ui:field": "SearchableSelectField",
|
|
319
|
+
"ui:options": {
|
|
320
|
+
placeholder: "Search...",
|
|
321
|
+
clearButtonText: "Clear",
|
|
322
|
+
expandText: "More results",
|
|
323
|
+
emptySearchMessage: "...no matching results found",
|
|
324
|
+
},
|
|
325
|
+
},
|
|
296
326
|
textArea: { "ui:field": "TextAreaField" },
|
|
297
327
|
};
|
|
@@ -214,4 +214,28 @@ export declare const Icon: import("styled-components").IStyledComponent<"web", i
|
|
|
214
214
|
export declare const CollapsibleChildWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
215
215
|
export declare const CollapsibleObjectFieldWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>, never>>;
|
|
216
216
|
export declare const CollapsibleObjectFieldTitle: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLLegendElement>, HTMLLegendElement>, never>>;
|
|
217
|
+
export declare const SearchableSelectContainer: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>, "ref"> & {
|
|
218
|
+
ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
|
|
219
|
+
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>, keyof React.Component<any, {}, any>>;
|
|
220
|
+
export declare const SearchDropdownContainer: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, never>, "ref"> & {
|
|
221
|
+
ref?: ((instance: HTMLUListElement | null) => void) | React.RefObject<HTMLUListElement> | null | undefined;
|
|
222
|
+
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, never>>, keyof React.Component<any, {}, any>>;
|
|
223
|
+
export declare const SearchListOption: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, never>, "ref"> & {
|
|
224
|
+
ref?: ((instance: HTMLLIElement | null) => void) | React.RefObject<HTMLLIElement> | null | undefined;
|
|
225
|
+
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, never>>, keyof React.Component<any, {}, any>>;
|
|
226
|
+
export declare const SearchListExpand: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, never>, "ref"> & {
|
|
227
|
+
ref?: ((instance: HTMLLIElement | null) => void) | React.RefObject<HTMLLIElement> | null | undefined;
|
|
228
|
+
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, never>>, keyof React.Component<any, {}, any>>;
|
|
229
|
+
export declare const SearchNotFound: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, never>, "ref"> & {
|
|
230
|
+
ref?: ((instance: HTMLLIElement | null) => void) | React.RefObject<HTMLLIElement> | null | undefined;
|
|
231
|
+
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, never>>, keyof React.Component<any, {}, any>>;
|
|
232
|
+
export declare const SearchInput: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<Omit<Omit<any, "as">, "ref"> & {
|
|
233
|
+
ref?: unknown;
|
|
234
|
+
}, never>, "ref"> & {
|
|
235
|
+
ref?: unknown;
|
|
236
|
+
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<Omit<any, "as">, "ref"> & {
|
|
237
|
+
ref?: unknown;
|
|
238
|
+
}, never>> & Omit<({ formikAs, ...props }: Omit<formik.FieldAttributes<any>, "as">) => import("react/jsx-runtime").JSX.Element, keyof React.Component<any, {}, any>>, keyof React.Component<any, {}, any>>;
|
|
239
|
+
export declare const SearchClearButton: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>>;
|
|
240
|
+
export declare const SearchInputContainer: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
217
241
|
export {};
|
package/dist/styledComponents.js
CHANGED
|
@@ -402,3 +402,31 @@ export const Icon = styled.div `
|
|
|
402
402
|
export const CollapsibleChildWrapper = styled.div ``;
|
|
403
403
|
export const CollapsibleObjectFieldWrapper = ObjectFieldWrapper;
|
|
404
404
|
export const CollapsibleObjectFieldTitle = ObjectFieldTitle;
|
|
405
|
+
export const SearchableSelectContainer = styled(MultiSelectContainer) ``;
|
|
406
|
+
export const SearchDropdownContainer = styled(MultiSelectOptionList) `
|
|
407
|
+
overflow-y: auto;
|
|
408
|
+
overflow-x: auto;
|
|
409
|
+
max-width: 16rem;
|
|
410
|
+
max-height: 6rem;
|
|
411
|
+
white-space: nowrap;
|
|
412
|
+
`;
|
|
413
|
+
export const SearchListOption = styled(MultiSelectOptionListItem) `
|
|
414
|
+
cursor: default;
|
|
415
|
+
`;
|
|
416
|
+
export const SearchListExpand = styled(MultiSelectOptionListItem) `
|
|
417
|
+
text-decoration: underline;
|
|
418
|
+
&:hover {
|
|
419
|
+
background: none;
|
|
420
|
+
}
|
|
421
|
+
`;
|
|
422
|
+
export const SearchNotFound = styled(MultiSelectOptionListItem) `
|
|
423
|
+
cursor: default;
|
|
424
|
+
&:hover {
|
|
425
|
+
background: none;
|
|
426
|
+
}
|
|
427
|
+
`;
|
|
428
|
+
export const SearchInput = styled(FieldInput) `
|
|
429
|
+
text-overflow: ellipsis;
|
|
430
|
+
`;
|
|
431
|
+
export const SearchClearButton = styled.button ``;
|
|
432
|
+
export const SearchInputContainer = styled.div ``;
|
package/dist/types.d.ts
CHANGED
|
@@ -147,6 +147,14 @@ export type ComponentsType = {
|
|
|
147
147
|
BooleanFieldWrapper: React.FunctionComponent<any>;
|
|
148
148
|
CollapsibleObjectFieldWrapper: React.FunctionComponent<any>;
|
|
149
149
|
CollapsibleObjectFieldTitle: React.FunctionComponent<any>;
|
|
150
|
+
SearchableSelectContainer: React.FunctionComponent<any>;
|
|
151
|
+
SearchDropdownContainer: React.FunctionComponent<any>;
|
|
152
|
+
SearchListOption: React.FunctionComponent<any>;
|
|
153
|
+
SearchListExpand: React.FunctionComponent<any>;
|
|
154
|
+
SearchNotFound: React.FunctionComponent<any>;
|
|
155
|
+
SearchInput: React.FunctionComponent<any>;
|
|
156
|
+
SearchClearButton: React.FunctionComponent<any>;
|
|
157
|
+
SearchInputContainer: React.FunctionComponent<any>;
|
|
150
158
|
};
|
|
151
159
|
export declare enum ObjectFieldButtonPurpose {
|
|
152
160
|
ADD = 0,
|