@react-typed-forms/schemas 8.2.0 → 9.0.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/lib/controlBuilder.d.ts +9 -2
- package/lib/controlRender.d.ts +12 -11
- package/lib/hooks.d.ts +8 -8
- package/lib/index.js +323 -215
- package/lib/index.js.map +1 -1
- package/lib/renderers.d.ts +1 -0
- package/lib/schemaBuilder.d.ts +5 -2
- package/lib/types.d.ts +9 -1
- package/lib/util.d.ts +12 -0
- package/package.json +2 -2
- package/.babelrc +0 -4
- package/.rush/temp/operation/build/state.json +0 -3
- package/.rush/temp/operation/update-readme/state.json +0 -3
- package/.rush/temp/package-deps_build.json +0 -13
- package/.rush/temp/shrinkwrap-deps.json +0 -612
- package/src/controlBuilder.ts +0 -175
- package/src/controlRender.tsx +0 -790
- package/src/hooks.tsx +0 -373
- package/src/index.ts +0 -10
- package/src/internal.ts +0 -48
- package/src/renderers.tsx +0 -996
- package/src/schemaBuilder.ts +0 -171
- package/src/schemaInterface.ts +0 -31
- package/src/tailwind.tsx +0 -29
- package/src/types.ts +0 -423
- package/src/util.ts +0 -453
- package/src/validators.ts +0 -116
package/src/renderers.tsx
DELETED
|
@@ -1,996 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
CSSProperties,
|
|
3
|
-
Fragment,
|
|
4
|
-
ReactElement,
|
|
5
|
-
ReactNode,
|
|
6
|
-
useEffect,
|
|
7
|
-
useMemo,
|
|
8
|
-
useState,
|
|
9
|
-
} from "react";
|
|
10
|
-
import clsx from "clsx";
|
|
11
|
-
import { Control, Fcheckbox, formControlProps } from "@react-typed-forms/core";
|
|
12
|
-
import {
|
|
13
|
-
ActionRendererProps,
|
|
14
|
-
AdornmentProps,
|
|
15
|
-
AdornmentRenderer,
|
|
16
|
-
appendMarkupAt,
|
|
17
|
-
ArrayRendererProps,
|
|
18
|
-
ControlLayoutProps,
|
|
19
|
-
DataRendererProps,
|
|
20
|
-
DisplayRendererProps,
|
|
21
|
-
FormRenderer,
|
|
22
|
-
GroupRendererProps,
|
|
23
|
-
LabelRendererProps,
|
|
24
|
-
LabelType,
|
|
25
|
-
RenderedControl,
|
|
26
|
-
RenderedLayout,
|
|
27
|
-
renderLayoutParts,
|
|
28
|
-
VisibilityRendererProps,
|
|
29
|
-
} from "./controlRender";
|
|
30
|
-
import {
|
|
31
|
-
AdornmentPlacement,
|
|
32
|
-
ControlAdornment,
|
|
33
|
-
ControlAdornmentType,
|
|
34
|
-
ControlDefinitionType,
|
|
35
|
-
DataRenderType,
|
|
36
|
-
DisplayDataType,
|
|
37
|
-
FieldOption,
|
|
38
|
-
FieldType,
|
|
39
|
-
FlexRenderer,
|
|
40
|
-
GridRenderer,
|
|
41
|
-
HtmlDisplay,
|
|
42
|
-
IconAdornment,
|
|
43
|
-
IconDisplay,
|
|
44
|
-
isDisplayOnlyRenderer,
|
|
45
|
-
isFlexRenderer,
|
|
46
|
-
isGridRenderer,
|
|
47
|
-
SchemaField,
|
|
48
|
-
SchemaInterface,
|
|
49
|
-
TextDisplay,
|
|
50
|
-
} from "./types";
|
|
51
|
-
import { getOverrideClass, hasOptions, rendererClass } from "./util";
|
|
52
|
-
|
|
53
|
-
export interface DefaultRenderers {
|
|
54
|
-
data: DataRendererRegistration;
|
|
55
|
-
label: LabelRendererRegistration;
|
|
56
|
-
action: ActionRendererRegistration;
|
|
57
|
-
array: ArrayRendererRegistration;
|
|
58
|
-
group: GroupRendererRegistration;
|
|
59
|
-
display: DisplayRendererRegistration;
|
|
60
|
-
adornment: AdornmentRendererRegistration;
|
|
61
|
-
renderLayout: LayoutRendererRegistration;
|
|
62
|
-
visibility: VisibilityRendererRegistration;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface LayoutRendererRegistration {
|
|
66
|
-
type: "layout";
|
|
67
|
-
match?: (props: ControlLayoutProps) => boolean;
|
|
68
|
-
render: (
|
|
69
|
-
props: ControlLayoutProps,
|
|
70
|
-
renderers: FormRenderer,
|
|
71
|
-
) => RenderedControl;
|
|
72
|
-
}
|
|
73
|
-
export interface DataRendererRegistration {
|
|
74
|
-
type: "data";
|
|
75
|
-
schemaType?: string | string[];
|
|
76
|
-
renderType?: string | string[];
|
|
77
|
-
options?: boolean;
|
|
78
|
-
collection?: boolean;
|
|
79
|
-
match?: (props: DataRendererProps) => boolean;
|
|
80
|
-
render: (
|
|
81
|
-
props: DataRendererProps,
|
|
82
|
-
renderers: FormRenderer,
|
|
83
|
-
) => ReactNode | ((layout: ControlLayoutProps) => ControlLayoutProps);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface LabelRendererRegistration {
|
|
87
|
-
type: "label";
|
|
88
|
-
labelType?: LabelType | LabelType[];
|
|
89
|
-
render: (
|
|
90
|
-
labelProps: LabelRendererProps,
|
|
91
|
-
labelStart: ReactNode,
|
|
92
|
-
labelEnd: ReactNode,
|
|
93
|
-
renderers: FormRenderer,
|
|
94
|
-
) => ReactElement;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface ActionRendererRegistration {
|
|
98
|
-
type: "action";
|
|
99
|
-
actionType?: string | string[];
|
|
100
|
-
render: (props: ActionRendererProps, renderers: FormRenderer) => ReactElement;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface ArrayRendererRegistration {
|
|
104
|
-
type: "array";
|
|
105
|
-
render: (props: ArrayRendererProps, renderers: FormRenderer) => ReactElement;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface GroupRendererRegistration {
|
|
109
|
-
type: "group";
|
|
110
|
-
renderType?: string | string[];
|
|
111
|
-
render: (
|
|
112
|
-
props: GroupRendererProps,
|
|
113
|
-
renderers: FormRenderer,
|
|
114
|
-
) => ReactElement | ((layout: ControlLayoutProps) => ControlLayoutProps);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export interface DisplayRendererRegistration {
|
|
118
|
-
type: "display";
|
|
119
|
-
renderType?: string | string[];
|
|
120
|
-
render: (
|
|
121
|
-
props: DisplayRendererProps,
|
|
122
|
-
renderers: FormRenderer,
|
|
123
|
-
) => ReactElement;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface AdornmentRendererRegistration {
|
|
127
|
-
type: "adornment";
|
|
128
|
-
adornmentType?: string | string[];
|
|
129
|
-
render: (props: AdornmentProps) => AdornmentRenderer;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export interface VisibilityRendererRegistration {
|
|
133
|
-
type: "visibility";
|
|
134
|
-
render: (props: VisibilityRendererProps) => ReactNode;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export type RendererRegistration =
|
|
138
|
-
| DataRendererRegistration
|
|
139
|
-
| GroupRendererRegistration
|
|
140
|
-
| DisplayRendererRegistration
|
|
141
|
-
| ActionRendererRegistration
|
|
142
|
-
| LabelRendererRegistration
|
|
143
|
-
| ArrayRendererRegistration
|
|
144
|
-
| AdornmentRendererRegistration
|
|
145
|
-
| LayoutRendererRegistration
|
|
146
|
-
| VisibilityRendererRegistration;
|
|
147
|
-
|
|
148
|
-
export function createFormRenderer(
|
|
149
|
-
customRenderers: RendererRegistration[] = [],
|
|
150
|
-
defaultRenderers: DefaultRenderers = createClassStyledRenderers(),
|
|
151
|
-
): FormRenderer {
|
|
152
|
-
const dataRegistrations = customRenderers.filter(isDataRegistration);
|
|
153
|
-
const groupRegistrations = customRenderers.filter(isGroupRegistration);
|
|
154
|
-
const adornmentRegistrations = customRenderers.filter(
|
|
155
|
-
isAdornmentRegistration,
|
|
156
|
-
);
|
|
157
|
-
const displayRegistrations = customRenderers.filter(isDisplayRegistration);
|
|
158
|
-
const labelRenderers = customRenderers.filter(isLabelRegistration);
|
|
159
|
-
const arrayRenderers = customRenderers.filter(isArrayRegistration);
|
|
160
|
-
const actionRenderers = customRenderers.filter(isActionRegistration);
|
|
161
|
-
const layoutRenderers = customRenderers.filter(isLayoutRegistration);
|
|
162
|
-
const visibilityRenderer =
|
|
163
|
-
customRenderers.find(isVisibilityRegistration) ??
|
|
164
|
-
defaultRenderers.visibility;
|
|
165
|
-
|
|
166
|
-
const formRenderers: FormRenderer = {
|
|
167
|
-
renderAction,
|
|
168
|
-
renderData,
|
|
169
|
-
renderGroup,
|
|
170
|
-
renderDisplay,
|
|
171
|
-
renderLabel,
|
|
172
|
-
renderArray,
|
|
173
|
-
renderAdornment,
|
|
174
|
-
renderLayout,
|
|
175
|
-
renderVisibility: visibilityRenderer.render,
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
function renderLayout(props: ControlLayoutProps) {
|
|
179
|
-
const renderer =
|
|
180
|
-
layoutRenderers.find((x) => !x.match || x.match(props)) ??
|
|
181
|
-
defaultRenderers.renderLayout;
|
|
182
|
-
return renderer.render(props, formRenderers);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function renderAdornment(props: AdornmentProps): AdornmentRenderer {
|
|
186
|
-
const renderer =
|
|
187
|
-
adornmentRegistrations.find((x) =>
|
|
188
|
-
isOneOf(x.adornmentType, props.adornment.type),
|
|
189
|
-
) ?? defaultRenderers.adornment;
|
|
190
|
-
return renderer.render(props);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function renderArray(props: ArrayRendererProps) {
|
|
194
|
-
return (arrayRenderers[0] ?? defaultRenderers.array).render(
|
|
195
|
-
props,
|
|
196
|
-
formRenderers,
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function renderLabel(
|
|
201
|
-
props: LabelRendererProps,
|
|
202
|
-
labelStart: ReactNode,
|
|
203
|
-
labelEnd: ReactNode,
|
|
204
|
-
) {
|
|
205
|
-
const renderer =
|
|
206
|
-
labelRenderers.find((x) => isOneOf(x.labelType, props.type)) ??
|
|
207
|
-
defaultRenderers.label;
|
|
208
|
-
return renderer.render(props, labelStart, labelEnd, formRenderers);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function renderData(
|
|
212
|
-
props: DataRendererProps,
|
|
213
|
-
): (layout: ControlLayoutProps) => ControlLayoutProps {
|
|
214
|
-
const {
|
|
215
|
-
renderOptions: { type: renderType },
|
|
216
|
-
field,
|
|
217
|
-
} = props;
|
|
218
|
-
|
|
219
|
-
const options = hasOptions(props);
|
|
220
|
-
const renderer =
|
|
221
|
-
dataRegistrations.find(
|
|
222
|
-
(x) =>
|
|
223
|
-
(x.collection ?? false) === (field.collection ?? false) &&
|
|
224
|
-
(x.options ?? false) === options &&
|
|
225
|
-
isOneOf(x.schemaType, field.type) &&
|
|
226
|
-
isOneOf(x.renderType, renderType) &&
|
|
227
|
-
(!x.match || x.match(props)),
|
|
228
|
-
) ?? defaultRenderers.data;
|
|
229
|
-
|
|
230
|
-
const result = renderer.render(props, formRenderers);
|
|
231
|
-
if (typeof result === "function") return result;
|
|
232
|
-
return (l) => ({ ...l, children: result });
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function renderGroup(
|
|
236
|
-
props: GroupRendererProps,
|
|
237
|
-
): (layout: ControlLayoutProps) => ControlLayoutProps {
|
|
238
|
-
const renderType = props.renderOptions.type;
|
|
239
|
-
const renderer =
|
|
240
|
-
groupRegistrations.find((x) => isOneOf(x.renderType, renderType)) ??
|
|
241
|
-
defaultRenderers.group;
|
|
242
|
-
const result = renderer.render(props, formRenderers);
|
|
243
|
-
if (typeof result === "function") return result;
|
|
244
|
-
return (l) => ({ ...l, children: result });
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function renderAction(props: ActionRendererProps) {
|
|
248
|
-
const renderer =
|
|
249
|
-
actionRenderers.find((x) => isOneOf(x.actionType, props.actionId)) ??
|
|
250
|
-
defaultRenderers.action;
|
|
251
|
-
return renderer.render(props, formRenderers);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function renderDisplay(props: DisplayRendererProps) {
|
|
255
|
-
const renderType = props.data.type;
|
|
256
|
-
const renderer =
|
|
257
|
-
displayRegistrations.find((x) => isOneOf(x.renderType, renderType)) ??
|
|
258
|
-
defaultRenderers.display;
|
|
259
|
-
return renderer.render(props, formRenderers);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return formRenderers;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
interface DefaultLabelRendererOptions {
|
|
266
|
-
className?: string;
|
|
267
|
-
groupLabelClass?: string;
|
|
268
|
-
controlLabelClass?: string;
|
|
269
|
-
requiredElement?: ReactNode;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
interface DefaultActionRendererOptions {
|
|
273
|
-
className?: string;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export function createDefaultActionRenderer(
|
|
277
|
-
options: DefaultActionRendererOptions = {},
|
|
278
|
-
): ActionRendererRegistration {
|
|
279
|
-
function render({ onClick, actionText }: ActionRendererProps) {
|
|
280
|
-
return (
|
|
281
|
-
<button className={options.className} onClick={onClick}>
|
|
282
|
-
{actionText}
|
|
283
|
-
</button>
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
return { render, type: "action" };
|
|
287
|
-
}
|
|
288
|
-
export function createDefaultLabelRenderer(
|
|
289
|
-
options: DefaultLabelRendererOptions = { requiredElement: <span> *</span> },
|
|
290
|
-
): LabelRendererRegistration {
|
|
291
|
-
const { className, groupLabelClass, controlLabelClass, requiredElement } =
|
|
292
|
-
options;
|
|
293
|
-
return {
|
|
294
|
-
render: (props, labelStart, labelEnd) => (
|
|
295
|
-
<>
|
|
296
|
-
{labelStart}
|
|
297
|
-
<label
|
|
298
|
-
htmlFor={props.forId}
|
|
299
|
-
className={rendererClass(
|
|
300
|
-
props.className,
|
|
301
|
-
clsx(
|
|
302
|
-
className,
|
|
303
|
-
props.type === LabelType.Group && groupLabelClass,
|
|
304
|
-
props.type === LabelType.Control && controlLabelClass,
|
|
305
|
-
),
|
|
306
|
-
)}
|
|
307
|
-
>
|
|
308
|
-
{props.label}
|
|
309
|
-
{props.required && requiredElement}
|
|
310
|
-
</label>
|
|
311
|
-
{labelEnd}
|
|
312
|
-
</>
|
|
313
|
-
),
|
|
314
|
-
type: "label",
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
interface DefaultArrayRendererOptions {
|
|
319
|
-
className?: string;
|
|
320
|
-
removableClass?: string;
|
|
321
|
-
childClass?: string;
|
|
322
|
-
removableChildClass?: string;
|
|
323
|
-
removeActionClass?: string;
|
|
324
|
-
addActionClass?: string;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export function createDefaultArrayRenderer(
|
|
328
|
-
options?: DefaultArrayRendererOptions,
|
|
329
|
-
): ArrayRendererRegistration {
|
|
330
|
-
const {
|
|
331
|
-
className,
|
|
332
|
-
removableClass,
|
|
333
|
-
childClass,
|
|
334
|
-
removableChildClass,
|
|
335
|
-
removeActionClass,
|
|
336
|
-
addActionClass,
|
|
337
|
-
} = options ?? {};
|
|
338
|
-
function render(
|
|
339
|
-
{
|
|
340
|
-
elementCount,
|
|
341
|
-
renderElement,
|
|
342
|
-
addAction,
|
|
343
|
-
removeAction,
|
|
344
|
-
elementKey,
|
|
345
|
-
required,
|
|
346
|
-
}: ArrayRendererProps,
|
|
347
|
-
{ renderAction }: FormRenderer,
|
|
348
|
-
) {
|
|
349
|
-
const showRemove = !required || elementCount > 1;
|
|
350
|
-
return (
|
|
351
|
-
<div>
|
|
352
|
-
<div className={clsx(className, removeAction && removableClass)}>
|
|
353
|
-
{Array.from({ length: elementCount }, (_, x) =>
|
|
354
|
-
removeAction ? (
|
|
355
|
-
<Fragment key={elementKey(x)}>
|
|
356
|
-
<div className={clsx(childClass, removableChildClass)}>
|
|
357
|
-
{renderElement(x)}
|
|
358
|
-
</div>
|
|
359
|
-
<div className={removeActionClass}>
|
|
360
|
-
{showRemove && renderAction(removeAction(x))}
|
|
361
|
-
</div>
|
|
362
|
-
</Fragment>
|
|
363
|
-
) : (
|
|
364
|
-
<div key={elementKey(x)} className={childClass}>
|
|
365
|
-
{renderElement(x)}
|
|
366
|
-
</div>
|
|
367
|
-
),
|
|
368
|
-
)}
|
|
369
|
-
</div>
|
|
370
|
-
{addAction && (
|
|
371
|
-
<div className={addActionClass}>{renderAction(addAction)}</div>
|
|
372
|
-
)}
|
|
373
|
-
</div>
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
return { render, type: "array" };
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
interface StyleProps {
|
|
380
|
-
className?: string;
|
|
381
|
-
style?: CSSProperties;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
interface DefaultGroupRendererOptions {
|
|
385
|
-
className?: string;
|
|
386
|
-
standardClassName?: string;
|
|
387
|
-
gridStyles?: (columns: GridRenderer) => StyleProps;
|
|
388
|
-
gridClassName?: string;
|
|
389
|
-
defaultGridColumns?: number;
|
|
390
|
-
flexClassName?: string;
|
|
391
|
-
defaultFlexGap?: string;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
export function createDefaultGroupRenderer(
|
|
395
|
-
options?: DefaultGroupRendererOptions,
|
|
396
|
-
): GroupRendererRegistration {
|
|
397
|
-
const {
|
|
398
|
-
className,
|
|
399
|
-
gridStyles = defaultGridStyles,
|
|
400
|
-
defaultGridColumns = 2,
|
|
401
|
-
gridClassName,
|
|
402
|
-
standardClassName,
|
|
403
|
-
flexClassName,
|
|
404
|
-
defaultFlexGap,
|
|
405
|
-
} = options ?? {};
|
|
406
|
-
|
|
407
|
-
function defaultGridStyles({
|
|
408
|
-
columns = defaultGridColumns,
|
|
409
|
-
}: GridRenderer): StyleProps {
|
|
410
|
-
return {
|
|
411
|
-
className: gridClassName,
|
|
412
|
-
style: {
|
|
413
|
-
display: "grid",
|
|
414
|
-
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
415
|
-
},
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function flexStyles(options: FlexRenderer): StyleProps {
|
|
420
|
-
return {
|
|
421
|
-
className: flexClassName,
|
|
422
|
-
style: {
|
|
423
|
-
display: "flex",
|
|
424
|
-
gap: options.gap ? options.gap : defaultFlexGap,
|
|
425
|
-
flexDirection: options.direction
|
|
426
|
-
? (options.direction as any)
|
|
427
|
-
: undefined,
|
|
428
|
-
},
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
function render(props: GroupRendererProps) {
|
|
433
|
-
const { renderChild, renderOptions, children } = props;
|
|
434
|
-
|
|
435
|
-
const { style, className: gcn } = isGridRenderer(renderOptions)
|
|
436
|
-
? gridStyles(renderOptions)
|
|
437
|
-
: isFlexRenderer(renderOptions)
|
|
438
|
-
? flexStyles(renderOptions)
|
|
439
|
-
: ({ className: standardClassName } as StyleProps);
|
|
440
|
-
|
|
441
|
-
return (cp: ControlLayoutProps) => {
|
|
442
|
-
return {
|
|
443
|
-
...cp,
|
|
444
|
-
children: (
|
|
445
|
-
<div
|
|
446
|
-
className={rendererClass(props.className, clsx(className, gcn))}
|
|
447
|
-
style={style}
|
|
448
|
-
>
|
|
449
|
-
{children?.map((c, i) => renderChild(i, i))}
|
|
450
|
-
</div>
|
|
451
|
-
),
|
|
452
|
-
};
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
return { type: "group", render };
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
export interface DefaultDisplayRendererOptions {
|
|
459
|
-
textClassName?: string;
|
|
460
|
-
htmlClassName?: string;
|
|
461
|
-
}
|
|
462
|
-
export function createDefaultDisplayRenderer(
|
|
463
|
-
options: DefaultDisplayRendererOptions = {},
|
|
464
|
-
): DisplayRendererRegistration {
|
|
465
|
-
return {
|
|
466
|
-
render: (props) => <DefaultDisplay {...options} {...props} />,
|
|
467
|
-
type: "display",
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
export function DefaultDisplay({
|
|
472
|
-
data,
|
|
473
|
-
display,
|
|
474
|
-
className,
|
|
475
|
-
style,
|
|
476
|
-
...options
|
|
477
|
-
}: DefaultDisplayRendererOptions & DisplayRendererProps) {
|
|
478
|
-
switch (data.type) {
|
|
479
|
-
case DisplayDataType.Icon:
|
|
480
|
-
return (
|
|
481
|
-
<i
|
|
482
|
-
style={style}
|
|
483
|
-
className={clsx(
|
|
484
|
-
getOverrideClass(className),
|
|
485
|
-
display ? display.value : (data as IconDisplay).iconClass,
|
|
486
|
-
)}
|
|
487
|
-
/>
|
|
488
|
-
);
|
|
489
|
-
case DisplayDataType.Text:
|
|
490
|
-
return (
|
|
491
|
-
<div
|
|
492
|
-
style={style}
|
|
493
|
-
className={rendererClass(className, options.textClassName)}
|
|
494
|
-
>
|
|
495
|
-
{display ? display.value : (data as TextDisplay).text}
|
|
496
|
-
</div>
|
|
497
|
-
);
|
|
498
|
-
case DisplayDataType.Html:
|
|
499
|
-
return (
|
|
500
|
-
<div
|
|
501
|
-
style={style}
|
|
502
|
-
className={rendererClass(className, options.htmlClassName)}
|
|
503
|
-
dangerouslySetInnerHTML={{
|
|
504
|
-
__html: display ? display.value ?? "" : (data as HtmlDisplay).html,
|
|
505
|
-
}}
|
|
506
|
-
/>
|
|
507
|
-
);
|
|
508
|
-
default:
|
|
509
|
-
return <h1>Unknown display type: {data.type}</h1>;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
export const DefaultBoolOptions: FieldOption[] = [
|
|
514
|
-
{ name: "Yes", value: true },
|
|
515
|
-
{ name: "No", value: false },
|
|
516
|
-
];
|
|
517
|
-
interface DefaultDataRendererOptions {
|
|
518
|
-
inputClass?: string;
|
|
519
|
-
displayOnlyClass?: string;
|
|
520
|
-
selectOptions?: SelectRendererOptions;
|
|
521
|
-
booleanOptions?: FieldOption[];
|
|
522
|
-
optionRenderer?: DataRendererRegistration;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
export function createDefaultDataRenderer(
|
|
526
|
-
options: DefaultDataRendererOptions = {},
|
|
527
|
-
): DataRendererRegistration {
|
|
528
|
-
const selectRenderer = createSelectRenderer(options.selectOptions ?? {});
|
|
529
|
-
const { inputClass, booleanOptions, optionRenderer, displayOnlyClass } = {
|
|
530
|
-
optionRenderer: selectRenderer,
|
|
531
|
-
booleanOptions: DefaultBoolOptions,
|
|
532
|
-
...options,
|
|
533
|
-
};
|
|
534
|
-
return createDataRenderer((props, renderers) => {
|
|
535
|
-
const fieldType = props.field.type;
|
|
536
|
-
if (props.toArrayProps) {
|
|
537
|
-
return (p) => ({
|
|
538
|
-
...p,
|
|
539
|
-
children: renderers.renderArray(props.toArrayProps!()),
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
if (fieldType === FieldType.Compound) {
|
|
543
|
-
return renderers.renderGroup({
|
|
544
|
-
style: props.style,
|
|
545
|
-
className: props.className,
|
|
546
|
-
children: props.children,
|
|
547
|
-
renderOptions: { type: "Standard", hideTitle: true },
|
|
548
|
-
renderChild: props.renderChild,
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
const renderOptions = props.renderOptions;
|
|
552
|
-
let renderType = renderOptions.type;
|
|
553
|
-
if (fieldType == FieldType.Any) return <>No control for Any</>;
|
|
554
|
-
if (isDisplayOnlyRenderer(renderOptions))
|
|
555
|
-
return (p) => ({
|
|
556
|
-
...p,
|
|
557
|
-
className: displayOnlyClass,
|
|
558
|
-
children: (
|
|
559
|
-
<DefaultDisplayOnly
|
|
560
|
-
field={props.field}
|
|
561
|
-
schemaInterface={props.dataContext.schemaInterface}
|
|
562
|
-
control={props.control}
|
|
563
|
-
className={props.className}
|
|
564
|
-
style={props.style}
|
|
565
|
-
emptyText={renderOptions.emptyText}
|
|
566
|
-
/>
|
|
567
|
-
),
|
|
568
|
-
});
|
|
569
|
-
const isBool = fieldType === FieldType.Bool;
|
|
570
|
-
if (booleanOptions != null && isBool && props.options == null) {
|
|
571
|
-
return renderers.renderData({ ...props, options: booleanOptions });
|
|
572
|
-
}
|
|
573
|
-
if (renderType === DataRenderType.Standard && hasOptions(props)) {
|
|
574
|
-
return optionRenderer.render(props, renderers);
|
|
575
|
-
}
|
|
576
|
-
switch (renderType) {
|
|
577
|
-
case DataRenderType.Dropdown:
|
|
578
|
-
return selectRenderer.render(props, renderers);
|
|
579
|
-
}
|
|
580
|
-
return renderType === DataRenderType.Checkbox ? (
|
|
581
|
-
<Fcheckbox
|
|
582
|
-
style={props.style}
|
|
583
|
-
className={props.className}
|
|
584
|
-
control={props.control}
|
|
585
|
-
/>
|
|
586
|
-
) : (
|
|
587
|
-
<ControlInput
|
|
588
|
-
className={rendererClass(props.className, inputClass)}
|
|
589
|
-
style={props.style}
|
|
590
|
-
id={props.id}
|
|
591
|
-
readOnly={props.readonly}
|
|
592
|
-
control={props.control}
|
|
593
|
-
convert={createInputConversion(props.field.type)}
|
|
594
|
-
/>
|
|
595
|
-
);
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
export function DefaultDisplayOnly({
|
|
600
|
-
control,
|
|
601
|
-
className,
|
|
602
|
-
emptyText,
|
|
603
|
-
schemaInterface,
|
|
604
|
-
field,
|
|
605
|
-
style,
|
|
606
|
-
}: {
|
|
607
|
-
control: Control<any>;
|
|
608
|
-
field: SchemaField;
|
|
609
|
-
schemaInterface: SchemaInterface;
|
|
610
|
-
className?: string;
|
|
611
|
-
style?: React.CSSProperties;
|
|
612
|
-
emptyText?: string | null;
|
|
613
|
-
}) {
|
|
614
|
-
const v = control.value;
|
|
615
|
-
const text =
|
|
616
|
-
(schemaInterface.isEmptyValue(field, v)
|
|
617
|
-
? emptyText
|
|
618
|
-
: schemaInterface.textValue(field, v)) ?? "";
|
|
619
|
-
return (
|
|
620
|
-
<div style={style} className={rendererClass(className)}>
|
|
621
|
-
{text}
|
|
622
|
-
</div>
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
export function ControlInput({
|
|
627
|
-
control,
|
|
628
|
-
convert,
|
|
629
|
-
...props
|
|
630
|
-
}: React.InputHTMLAttributes<HTMLInputElement> & {
|
|
631
|
-
control: Control<any>;
|
|
632
|
-
convert: InputConversion;
|
|
633
|
-
}) {
|
|
634
|
-
const { errorText, value, onChange, ...inputProps } =
|
|
635
|
-
formControlProps(control);
|
|
636
|
-
return (
|
|
637
|
-
<input
|
|
638
|
-
{...inputProps}
|
|
639
|
-
type={convert[0]}
|
|
640
|
-
value={value == null ? "" : convert[2](value)}
|
|
641
|
-
onChange={(e) => {
|
|
642
|
-
control.value = convert[1](e.target.value);
|
|
643
|
-
}}
|
|
644
|
-
{...props}
|
|
645
|
-
/>
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
export interface DefaultAdornmentRendererOptions {}
|
|
650
|
-
|
|
651
|
-
export function createDefaultAdornmentRenderer(
|
|
652
|
-
options: DefaultAdornmentRendererOptions = {},
|
|
653
|
-
): AdornmentRendererRegistration {
|
|
654
|
-
return {
|
|
655
|
-
type: "adornment",
|
|
656
|
-
render: ({ adornment }) => ({
|
|
657
|
-
apply: (rl) => {
|
|
658
|
-
if (isIconAdornment(adornment)) {
|
|
659
|
-
return appendMarkupAt(
|
|
660
|
-
adornment.placement ?? AdornmentPlacement.ControlStart,
|
|
661
|
-
<i className={adornment.iconClass} />,
|
|
662
|
-
)(rl);
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
priority: 0,
|
|
666
|
-
adornment,
|
|
667
|
-
}),
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
export interface DefaultLayoutRendererOptions {
|
|
672
|
-
className?: string;
|
|
673
|
-
errorClass?: string;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
export interface DefaultRendererOptions {
|
|
677
|
-
data?: DefaultDataRendererOptions;
|
|
678
|
-
display?: DefaultDisplayRendererOptions;
|
|
679
|
-
action?: DefaultActionRendererOptions;
|
|
680
|
-
array?: DefaultArrayRendererOptions;
|
|
681
|
-
group?: DefaultGroupRendererOptions;
|
|
682
|
-
label?: DefaultLabelRendererOptions;
|
|
683
|
-
adornment?: DefaultAdornmentRendererOptions;
|
|
684
|
-
layout?: DefaultLayoutRendererOptions;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
export function createDefaultRenderers(
|
|
688
|
-
options: DefaultRendererOptions = {},
|
|
689
|
-
): DefaultRenderers {
|
|
690
|
-
return {
|
|
691
|
-
data: createDefaultDataRenderer(options.data),
|
|
692
|
-
display: createDefaultDisplayRenderer(options.display),
|
|
693
|
-
action: createDefaultActionRenderer(options.action),
|
|
694
|
-
array: createDefaultArrayRenderer(options.array),
|
|
695
|
-
group: createDefaultGroupRenderer(options.group),
|
|
696
|
-
label: createDefaultLabelRenderer(options.label),
|
|
697
|
-
adornment: createDefaultAdornmentRenderer(options.adornment),
|
|
698
|
-
renderLayout: createDefaultLayoutRenderer(options.layout),
|
|
699
|
-
visibility: createDefaultVisibilityRenderer(),
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
function createDefaultLayoutRenderer(
|
|
704
|
-
options: DefaultLayoutRendererOptions = {},
|
|
705
|
-
) {
|
|
706
|
-
return createLayoutRenderer((props, renderers) => {
|
|
707
|
-
const layout = renderLayoutParts(
|
|
708
|
-
{
|
|
709
|
-
...props,
|
|
710
|
-
className: rendererClass(props.className, options.className),
|
|
711
|
-
},
|
|
712
|
-
renderers,
|
|
713
|
-
);
|
|
714
|
-
return {
|
|
715
|
-
children: <DefaultLayout layout={layout} {...options} />,
|
|
716
|
-
className: layout.className,
|
|
717
|
-
style: layout.style,
|
|
718
|
-
divRef: (e) =>
|
|
719
|
-
e && props.errorControl
|
|
720
|
-
? (props.errorControl.meta.scrollElement = e)
|
|
721
|
-
: undefined,
|
|
722
|
-
};
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function createClassStyledRenderers() {
|
|
727
|
-
return createDefaultRenderers({
|
|
728
|
-
layout: { className: "control" },
|
|
729
|
-
group: { className: "group" },
|
|
730
|
-
array: { className: "control-array" },
|
|
731
|
-
action: { className: "action" },
|
|
732
|
-
data: { inputClass: "data" },
|
|
733
|
-
display: { htmlClassName: "html", textClassName: "text" },
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function isAdornmentRegistration(
|
|
738
|
-
x: RendererRegistration,
|
|
739
|
-
): x is AdornmentRendererRegistration {
|
|
740
|
-
return x.type === "adornment";
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
function isDataRegistration(
|
|
744
|
-
x: RendererRegistration,
|
|
745
|
-
): x is DataRendererRegistration {
|
|
746
|
-
return x.type === "data";
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
function isGroupRegistration(
|
|
750
|
-
x: RendererRegistration,
|
|
751
|
-
): x is GroupRendererRegistration {
|
|
752
|
-
return x.type === "group";
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
function isLabelRegistration(
|
|
756
|
-
x: RendererRegistration,
|
|
757
|
-
): x is LabelRendererRegistration {
|
|
758
|
-
return x.type === "label";
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
function isLayoutRegistration(
|
|
762
|
-
x: RendererRegistration,
|
|
763
|
-
): x is LayoutRendererRegistration {
|
|
764
|
-
return x.type === "layout";
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function isVisibilityRegistration(
|
|
768
|
-
x: RendererRegistration,
|
|
769
|
-
): x is VisibilityRendererRegistration {
|
|
770
|
-
return x.type === "visibility";
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
function isActionRegistration(
|
|
774
|
-
x: RendererRegistration,
|
|
775
|
-
): x is ActionRendererRegistration {
|
|
776
|
-
return x.type === "action";
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function isDisplayRegistration(
|
|
780
|
-
x: RendererRegistration,
|
|
781
|
-
): x is DisplayRendererRegistration {
|
|
782
|
-
return x.type === "display";
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function isArrayRegistration(
|
|
786
|
-
x: RendererRegistration,
|
|
787
|
-
): x is ArrayRendererRegistration {
|
|
788
|
-
return x.type === "array";
|
|
789
|
-
}
|
|
790
|
-
function isOneOf<A>(x: A | A[] | undefined, v: A) {
|
|
791
|
-
return x == null ? true : Array.isArray(x) ? x.includes(v) : v === x;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
export function isIconAdornment(a: ControlAdornment): a is IconAdornment {
|
|
795
|
-
return a.type === ControlAdornmentType.Icon;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
export function createLayoutRenderer(
|
|
799
|
-
render: LayoutRendererRegistration["render"],
|
|
800
|
-
options?: Partial<LayoutRendererRegistration>,
|
|
801
|
-
): LayoutRendererRegistration {
|
|
802
|
-
return { type: "layout", render, ...options };
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
export function createArrayRenderer(
|
|
806
|
-
render: ArrayRendererRegistration["render"],
|
|
807
|
-
options?: Partial<ArrayRendererRegistration>,
|
|
808
|
-
): ArrayRendererRegistration {
|
|
809
|
-
return { type: "array", render, ...options };
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
export function createDataRenderer(
|
|
813
|
-
render: DataRendererRegistration["render"],
|
|
814
|
-
options?: Partial<DataRendererRegistration>,
|
|
815
|
-
): DataRendererRegistration {
|
|
816
|
-
return { type: "data", render, ...options };
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
export function createLabelRenderer(
|
|
820
|
-
render: LabelRendererRegistration["render"],
|
|
821
|
-
options?: Omit<LabelRendererRegistration, "type">,
|
|
822
|
-
): LabelRendererRegistration {
|
|
823
|
-
return { type: "label", render, ...options };
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
export function createVisibilityRenderer(
|
|
827
|
-
render: VisibilityRendererRegistration["render"],
|
|
828
|
-
options?: Partial<VisibilityRendererRegistration>,
|
|
829
|
-
): VisibilityRendererRegistration {
|
|
830
|
-
return { type: "visibility", render, ...options };
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
export function createAdornmentRenderer(
|
|
834
|
-
render: (props: AdornmentProps) => AdornmentRenderer,
|
|
835
|
-
options?: Partial<AdornmentRendererRegistration>,
|
|
836
|
-
): AdornmentRendererRegistration {
|
|
837
|
-
return { type: "adornment", ...options, render };
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
export interface SelectRendererOptions {
|
|
841
|
-
className?: string;
|
|
842
|
-
emptyText?: string;
|
|
843
|
-
requiredText?: string;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
export function createSelectRenderer(options: SelectRendererOptions = {}) {
|
|
847
|
-
return createDataRenderer(
|
|
848
|
-
(props, asArray) => (
|
|
849
|
-
<SelectDataRenderer
|
|
850
|
-
className={rendererClass(props.className, options.className)}
|
|
851
|
-
state={props.control}
|
|
852
|
-
id={props.id}
|
|
853
|
-
options={props.options!}
|
|
854
|
-
required={props.required}
|
|
855
|
-
emptyText={options.emptyText}
|
|
856
|
-
requiredText={options.requiredText}
|
|
857
|
-
convert={createSelectConversion(props.field.type)}
|
|
858
|
-
/>
|
|
859
|
-
),
|
|
860
|
-
{
|
|
861
|
-
options: true,
|
|
862
|
-
},
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
type SelectConversion = (a: any) => string | number;
|
|
867
|
-
|
|
868
|
-
interface SelectDataRendererProps {
|
|
869
|
-
id?: string;
|
|
870
|
-
className?: string;
|
|
871
|
-
options: {
|
|
872
|
-
name: string;
|
|
873
|
-
value: any;
|
|
874
|
-
disabled?: boolean;
|
|
875
|
-
}[];
|
|
876
|
-
emptyText?: string;
|
|
877
|
-
requiredText?: string;
|
|
878
|
-
required: boolean;
|
|
879
|
-
state: Control<any>;
|
|
880
|
-
convert: SelectConversion;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
export function SelectDataRenderer({
|
|
884
|
-
state,
|
|
885
|
-
options,
|
|
886
|
-
className,
|
|
887
|
-
convert,
|
|
888
|
-
required,
|
|
889
|
-
emptyText = "N/A",
|
|
890
|
-
requiredText = "<please select>",
|
|
891
|
-
...props
|
|
892
|
-
}: SelectDataRendererProps) {
|
|
893
|
-
const { value, disabled } = state;
|
|
894
|
-
const [showEmpty] = useState(!required || value == null);
|
|
895
|
-
const optionStringMap = useMemo(
|
|
896
|
-
() => Object.fromEntries(options.map((x) => [convert(x.value), x.value])),
|
|
897
|
-
[options],
|
|
898
|
-
);
|
|
899
|
-
return (
|
|
900
|
-
<select
|
|
901
|
-
{...props}
|
|
902
|
-
className={className}
|
|
903
|
-
onChange={(v) => (state.value = optionStringMap[v.target.value])}
|
|
904
|
-
value={convert(value)}
|
|
905
|
-
disabled={disabled}
|
|
906
|
-
>
|
|
907
|
-
{showEmpty && (
|
|
908
|
-
<option value="">{required ? requiredText : emptyText}</option>
|
|
909
|
-
)}
|
|
910
|
-
{options.map((x, i) => (
|
|
911
|
-
<option key={i} value={convert(x.value)} disabled={x.disabled}>
|
|
912
|
-
{x.name}
|
|
913
|
-
</option>
|
|
914
|
-
))}
|
|
915
|
-
</select>
|
|
916
|
-
);
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
export function createSelectConversion(ft: string): SelectConversion {
|
|
920
|
-
switch (ft) {
|
|
921
|
-
case FieldType.String:
|
|
922
|
-
case FieldType.Int:
|
|
923
|
-
case FieldType.Double:
|
|
924
|
-
return (a) => a;
|
|
925
|
-
default:
|
|
926
|
-
return (a) => a?.toString() ?? "";
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
type InputConversion = [string, (s: any) => any, (a: any) => string | number];
|
|
931
|
-
|
|
932
|
-
export function createInputConversion(ft: string): InputConversion {
|
|
933
|
-
switch (ft) {
|
|
934
|
-
case FieldType.String:
|
|
935
|
-
return ["text", (a) => a, (a) => a];
|
|
936
|
-
case FieldType.Bool:
|
|
937
|
-
return ["text", (a) => a === "true", (a) => a?.toString() ?? ""];
|
|
938
|
-
case FieldType.Int:
|
|
939
|
-
return [
|
|
940
|
-
"number",
|
|
941
|
-
(a) => (a !== "" ? parseInt(a) : null),
|
|
942
|
-
(a) => (a == null ? "" : a),
|
|
943
|
-
];
|
|
944
|
-
case FieldType.Date:
|
|
945
|
-
return ["date", (a) => a, (a) => a];
|
|
946
|
-
case FieldType.Double:
|
|
947
|
-
return ["number", (a) => parseFloat(a), (a) => a];
|
|
948
|
-
default:
|
|
949
|
-
return ["text", (a) => a, (a) => a];
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
export function createDefaultVisibilityRenderer() {
|
|
954
|
-
return createVisibilityRenderer((props) => <DefaultVisibility {...props} />);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
export function DefaultVisibility({
|
|
958
|
-
visibility,
|
|
959
|
-
children,
|
|
960
|
-
className,
|
|
961
|
-
style,
|
|
962
|
-
divRef,
|
|
963
|
-
}: VisibilityRendererProps) {
|
|
964
|
-
const v = visibility.value;
|
|
965
|
-
useEffect(() => {
|
|
966
|
-
if (v) {
|
|
967
|
-
visibility.setValue((ex) => ({ visible: v.visible, showing: v.visible }));
|
|
968
|
-
}
|
|
969
|
-
}, [v?.visible]);
|
|
970
|
-
return v?.visible ? (
|
|
971
|
-
<div className={clsx(className)} style={style} ref={divRef}>
|
|
972
|
-
{children}
|
|
973
|
-
</div>
|
|
974
|
-
) : (
|
|
975
|
-
<></>
|
|
976
|
-
);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
export function DefaultLayout({
|
|
980
|
-
errorClass,
|
|
981
|
-
layout: { controlEnd, controlStart, label, children, errorControl },
|
|
982
|
-
}: DefaultLayoutRendererOptions & {
|
|
983
|
-
layout: RenderedLayout;
|
|
984
|
-
}) {
|
|
985
|
-
const ec = errorControl;
|
|
986
|
-
const errorText = ec && ec.touched ? ec.error : undefined;
|
|
987
|
-
return (
|
|
988
|
-
<>
|
|
989
|
-
{label}
|
|
990
|
-
{controlStart}
|
|
991
|
-
{children}
|
|
992
|
-
{errorText && <div className={errorClass}>{errorText}</div>}
|
|
993
|
-
{controlEnd}
|
|
994
|
-
</>
|
|
995
|
-
);
|
|
996
|
-
}
|