@@ -4,8 +4,14 @@ import {
44 Box ,
55 Button ,
66 ButtonProps ,
7+ Chip ,
78 CircularProgress ,
9+ FormControl ,
810 IconButton ,
11+ InputLabel ,
12+ MenuItem ,
13+ OutlinedSelectProps ,
14+ Select ,
915 Stack ,
1016 Tab ,
1117 Table ,
@@ -18,10 +24,11 @@ import {
1824} from "@mui/material" ;
1925import Form , { IChangeEvent } from "@rjsf/core" ;
2026import MuiForm from "@rjsf/mui" ;
21- import { Field , RJSFSchema , UiSchema } from "@rjsf/utils" ;
27+ import { Field , FieldProps , RJSFSchema , UiSchema } from "@rjsf/utils" ;
2228import { customizeValidator } from "@rjsf/validator-ajv8" ;
2329import { ErrorBoundary , Suspense } from "@suspensive/react" ;
2430import AjvDraft04 from "ajv-draft-04" ;
31+ import { JSONSchema7 } from "json-schema" ;
2532import * as React from "react" ;
2633import { useNavigate , useParams } from "react-router-dom" ;
2734import * as R from "remeda" ;
@@ -66,6 +73,75 @@ const FileField: Field = (p) => (
6673 />
6774) ;
6875
76+ type DescriptedEnum = { const : string ; title : string } ;
77+ type DescriptedEnumObject = Record < string , DescriptedEnum > ;
78+
79+ const SelectdChipRenderer : React . FC < { selectable : DescriptedEnumObject ; selected : string [ ] } > = ( { selectable, selected } ) => {
80+ const children = selected . map ( ( v ) => < Chip key = { v } label = { selectable [ v ] . title || "" } /> ) ;
81+ return < Stack sx = { { flexWrap : "wrap" } } direction = "row" spacing = { 0.5 } children = { children } /> ;
82+ } ;
83+
84+ const fieldPropsToSelectedProps = ( props : FieldProps ) : OutlinedSelectProps & { defaultValue : string [ ] } => {
85+ const {
86+ name,
87+ formData,
88+ autofocus : autoFocus ,
89+ readonly : readOnly ,
90+ onFocus : rawOnFocus ,
91+ onBlur : rawOnBlur ,
92+ onChange : rawOnChange ,
93+
94+ schema,
95+ errorSchema,
96+ uiSchema,
97+ idSchema,
98+ formContext,
99+ wasPropertyKeyModified,
100+ registry,
101+ rawErrors,
102+ hideError,
103+ idPrefix,
104+ idSeparator,
105+ color,
106+ ...rest
107+ } = props ;
108+ const data = {
109+ schema,
110+ errorSchema,
111+ uiSchema,
112+ idSchema,
113+ formContext,
114+ wasPropertyKeyModified,
115+ registry,
116+ rawErrors,
117+ hideError,
118+ idPrefix,
119+ idSeparator,
120+ } ;
121+ const onFocus = ( event : React . FocusEvent < HTMLInputElement > ) => rawOnFocus ( event . currentTarget . name , event . currentTarget . value ) ;
122+ const onBlur = ( event : React . FocusEvent < HTMLInputElement > ) => rawOnBlur ( event . currentTarget . name , event . currentTarget . value ) ;
123+ const onChange = ( event : React . ChangeEvent < HTMLInputElement > ) => rawOnChange ( event . target . value , undefined , event . target . name ) ;
124+ const sx : OutlinedSelectProps [ "sx" ] = color ? { color, borderColor : color } : { } ;
125+ const defaultValue = ( formData ? ( R . isArray ( formData ) ? formData : [ formData . toString ( ) ] ) : [ ] ) as string [ ] ;
126+ return R . addProp ( { ...rest , name, label : name , defaultValue, autoFocus, readOnly, onFocus, onBlur, onChange, sx } , "data-rjsf" , data ) ;
127+ } ;
128+
129+ const M2MSelect : Field = ErrorBoundary . with (
130+ { fallback : Common . Components . ErrorFallback } ,
131+ Suspense . with ( { fallback : < CircularProgress /> } , ( props ) => {
132+ const selectable = ( props . schema . items as JSONSchema7 ) . oneOf as DescriptedEnum [ ] ;
133+ const selectableListObj : DescriptedEnumObject = selectable . reduce ( ( a , i ) => ( { ...a , [ i . const ] : i } ) , { } as DescriptedEnumObject ) ;
134+ const children = selectable . map ( ( i ) => < MenuItem key = { i . const } value = { i . const } children = { i . title || i . const } /> ) ;
135+ const selectRenderer = ( selected : string [ ] ) => < SelectdChipRenderer selectable = { selectableListObj } selected = { selected } /> ;
136+ return (
137+ < FormControl fullWidth >
138+ < InputLabel id = { `${ props . name } -label` } children = { props . name } />
139+ < Select { ...fieldPropsToSelectedProps ( props ) } children = { children } multiple fullWidth renderValue = { selectRenderer } />
140+ </ FormControl >
141+ ) ;
142+ } )
143+ ) ;
144+
69145type ReadOnlyValueFieldStateType = {
70146 loading : boolean ;
71147 blob : Blob | null ;
@@ -293,7 +369,7 @@ const InnerAdminEditor: React.FC<AppResourceIdType & AdminEditorPropsType> = Err
293369 onSubmit = { onSubmitFunc }
294370 disabled = { disabled }
295371 showErrorList = { false }
296- fields = { { file : FileField } }
372+ fields = { { file : FileField , m2m_select : M2MSelect } }
297373 />
298374 </ Box >
299375 </ Stack >
0 commit comments