@react-typed-forms/schemas 7.3.2 → 8.1.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.
@@ -65,6 +65,7 @@ export declare function createFormRenderer(customRenderers?: RendererRegistratio
65
65
  interface DefaultLabelRendererOptions {
66
66
  className?: string;
67
67
  groupLabelClass?: string;
68
+ controlLabelClass?: string;
68
69
  requiredElement?: ReactNode;
69
70
  }
70
71
  interface DefaultActionRendererOptions {
@@ -174,7 +175,7 @@ type InputConversion = [string, (s: any) => any, (a: any) => string | number];
174
175
  export declare function createInputConversion(ft: string): InputConversion;
175
176
  export declare function createDefaultVisibilityRenderer(): VisibilityRendererRegistration;
176
177
  export declare function DefaultVisibility({ visibility, children, className, style, divRef, }: VisibilityRendererProps): React.JSX.Element;
177
- export declare function DefaultLayout({ errorClass, className, layout: { controlEnd, controlStart, label, children, errorControl }, }: DefaultLayoutRendererOptions & {
178
+ export declare function DefaultLayout({ errorClass, layout: { controlEnd, controlStart, label, children, errorControl }, }: DefaultLayoutRendererOptions & {
178
179
  layout: RenderedLayout;
179
180
  }): React.JSX.Element;
180
181
  export {};
@@ -87,4 +87,5 @@ export declare function defaultScalarField(field: string, displayName: string):
87
87
  export declare function defaultCompoundField(field: string, displayName: string, collection: boolean): CompoundField & {
88
88
  type: FieldType.Compound;
89
89
  };
90
+ export declare function mergeField(field: SchemaField, mergeInto: SchemaField[]): SchemaField[];
90
91
  export {};
package/lib/tailwind.d.ts CHANGED
@@ -1,2 +1,27 @@
1
- import { DefaultRendererOptions } from "./renderers";
2
- export declare const defaultTailwindTheme: DefaultRendererOptions;
1
+ import React from "react";
2
+ export declare const defaultTailwindTheme: {
3
+ label: {
4
+ groupLabelClass: string;
5
+ requiredElement: React.JSX.Element;
6
+ };
7
+ array: {
8
+ removableClass: string;
9
+ childClass: string;
10
+ addActionClass: string;
11
+ };
12
+ group: {
13
+ standardClassName: string;
14
+ gridClassName: string;
15
+ flexClassName: string;
16
+ };
17
+ action: {
18
+ className: string;
19
+ };
20
+ layout: {
21
+ className: string;
22
+ errorClass: string;
23
+ };
24
+ data: {
25
+ displayOnlyClass: string;
26
+ };
27
+ };
package/lib/types.d.ts CHANGED
@@ -51,6 +51,7 @@ export interface ControlDefinition {
51
51
  title?: string | null;
52
52
  styleClass?: string | null;
53
53
  layoutClass?: string | null;
54
+ labelClass?: string | null;
54
55
  dynamic?: DynamicProperty[] | null;
55
56
  adornments?: ControlAdornment[] | null;
56
57
  children?: ControlDefinition[] | null;
@@ -73,7 +74,8 @@ export declare enum DynamicPropertyType {
73
74
  Display = "Display",
74
75
  Style = "Style",
75
76
  LayoutStyle = "LayoutStyle",
76
- AllowedOptions = "AllowedOptions"
77
+ AllowedOptions = "AllowedOptions",
78
+ Label = "Label"
77
79
  }
78
80
  export interface EntityExpression {
79
81
  type: string;
package/lib/util.d.ts CHANGED
@@ -38,4 +38,7 @@ export declare function cleanDataForSchema(v: {
38
38
  [k: string]: any;
39
39
  } | undefined, fields: SchemaField[]): any;
40
40
  export declare function getAllReferencedClasses(c: ControlDefinition): string[];
41
- export declare function jsonPathString(jsonPath: JsonPath[]): string;
41
+ export declare function jsonPathString(jsonPath: JsonPath[], customIndex?: (n: number) => string): string;
42
+ export declare function findChildDefinition(parent: ControlDefinition, childPath: number | number[]): ControlDefinition;
43
+ export declare function getOverrideClass(className?: string | null): string | null | undefined;
44
+ export declare function rendererClass(controlClass?: string | null, globalClass?: string | null): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-typed-forms/schemas",
3
- "version": "7.3.2",
3
+ "version": "8.1.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CompoundField,
2
3
  ControlDefinition,
3
4
  ControlDefinitionType,
4
5
  DataControlDefinition,
@@ -18,7 +19,8 @@ import {
18
19
  } from "./types";
19
20
  import { ActionRendererProps } from "./controlRender";
20
21
  import { useMemo } from "react";
21
- import { addMissingControls } from "./util";
22
+ import { addMissingControls, isCompoundField } from "./util";
23
+ import { mergeField } from "./schemaBuilder";
22
24
 
23
25
  export function dataControl(
24
26
  field: string,
@@ -128,3 +130,46 @@ export function useControlDefinitionForSchema(
128
130
  [sf, definition],
129
131
  );
130
132
  }
133
+
134
+ export interface CustomRenderOptions {
135
+ value: string;
136
+ name: string;
137
+ fields: SchemaField[];
138
+ }
139
+
140
+ export function addCustomDataRenderOptions(
141
+ controlFields: SchemaField[],
142
+ customRenderOptions: CustomRenderOptions[],
143
+ ): SchemaField[] {
144
+ return controlFields.map((x) =>
145
+ x.field === "renderOptions" && isCompoundField(x) ? addRenderOptions(x) : x,
146
+ );
147
+
148
+ function addRenderOptions(roField: CompoundField): CompoundField {
149
+ const children = roField.children;
150
+ const withTypes = children.map((x) =>
151
+ x.field === "type" ? addRenderOptionType(x) : x,
152
+ );
153
+ return {
154
+ ...roField,
155
+ children: customRenderOptions.reduce(
156
+ (renderOptionFields, ro) =>
157
+ ro.fields
158
+ .map((x) => ({ ...x, onlyForTypes: [ro.value] }))
159
+ .reduce((af, x) => mergeField(x, af), renderOptionFields),
160
+ withTypes,
161
+ ),
162
+ };
163
+ }
164
+
165
+ function addRenderOptionType(typeField: SchemaField): SchemaField {
166
+ const options = typeField.options ?? [];
167
+ return {
168
+ ...typeField,
169
+ options: [
170
+ ...options,
171
+ ...customRenderOptions.map(({ name, value }) => ({ name, value })),
172
+ ],
173
+ };
174
+ }
175
+ }
@@ -26,6 +26,7 @@ import {
26
26
  DisplayData,
27
27
  DynamicPropertyType,
28
28
  FieldOption,
29
+ GroupedControlsDefinition,
29
30
  GroupRenderOptions,
30
31
  isActionControlsDefinition,
31
32
  isDataControlDefinition,
@@ -39,6 +40,7 @@ import {
39
40
  ControlDataContext,
40
41
  elementValueForField,
41
42
  fieldDisplayName,
43
+ findChildDefinition,
42
44
  findField,
43
45
  isCompoundField,
44
46
  useUpdatedRef,
@@ -51,6 +53,7 @@ import {
51
53
  useEvalDisabledHook,
52
54
  useEvalDisplayHook,
53
55
  UseEvalExpressionHook,
56
+ useEvalLabelText,
54
57
  useEvalReadonlyHook,
55
58
  useEvalStyleHook,
56
59
  useEvalVisibilityHook,
@@ -99,7 +102,7 @@ export interface ArrayRendererProps {
99
102
  elementCount: number;
100
103
  renderElement: (elemIndex: number) => ReactNode;
101
104
  elementKey: (elemIndex: number) => Key;
102
- arrayControl?: Control<any[] | undefined | null>;
105
+ arrayControl: Control<any[] | undefined | null>;
103
106
  className?: string;
104
107
  style?: React.CSSProperties;
105
108
  }
@@ -151,6 +154,7 @@ export interface LabelRendererProps {
151
154
  label: ReactNode;
152
155
  required?: boolean | null;
153
156
  forId?: string;
157
+ className?: string;
154
158
  }
155
159
  export interface DisplayRendererProps {
156
160
  data: DisplayData;
@@ -160,15 +164,14 @@ export interface DisplayRendererProps {
160
164
  }
161
165
 
162
166
  export interface GroupRendererProps {
167
+ children: ControlDefinition[];
163
168
  renderOptions: GroupRenderOptions;
164
- childCount: number;
165
- renderChild: (child: number) => ReactNode;
169
+ renderChild: ChildRenderer;
166
170
  className?: string;
167
171
  style?: React.CSSProperties;
168
172
  }
169
173
 
170
174
  export interface DataRendererProps {
171
- definition: DataControlDefinition;
172
175
  renderOptions: RenderOptions;
173
176
  field: SchemaField;
174
177
  id: string;
@@ -180,7 +183,7 @@ export interface DataRendererProps {
180
183
  className?: string;
181
184
  style?: React.CSSProperties;
182
185
  dataContext: ControlDataContext;
183
- childCount: number;
186
+ children: ControlDefinition[];
184
187
  renderChild: ChildRenderer;
185
188
  toArrayProps?: () => ArrayRendererProps;
186
189
  }
@@ -211,7 +214,6 @@ export interface DataControlProps {
211
214
  control: Control<any>;
212
215
  options: FormContextOptions;
213
216
  style: React.CSSProperties | undefined;
214
- childCount: number;
215
217
  renderChild: ChildRenderer;
216
218
  allowedOptions?: Control<any[] | undefined>;
217
219
  elementRenderer?: (elemIndex: number) => ReactNode;
@@ -252,6 +254,7 @@ export function useControlRenderer(
252
254
  const useIsReadonly = useEvalReadonlyHook(useExpr, definition);
253
255
  const useIsDisabled = useEvalDisabledHook(useExpr, definition);
254
256
  const useAllowedOptions = useEvalAllowedOptionsHook(useExpr, definition);
257
+ const useLabelText = useEvalLabelText(useExpr, definition);
255
258
  const useCustomStyle = useEvalStyleHook(
256
259
  useExpr,
257
260
  DynamicPropertyType.Style,
@@ -283,6 +286,7 @@ export function useControlRenderer(
283
286
  const displayControl = useDynamicDisplay(parentDataContext);
284
287
  const customStyle = useCustomStyle(parentDataContext).value;
285
288
  const layoutStyle = useLayoutStyle(parentDataContext).value;
289
+ const labelText = useLabelText(parentDataContext);
286
290
  const visible = visibleControl.current.value;
287
291
  const visibility = useControl<Visibility | undefined>(() =>
288
292
  visible != null
@@ -343,13 +347,7 @@ export function useControlRenderer(
343
347
  !!myOptions.hidden,
344
348
  parentDataContext,
345
349
  );
346
- const childRenderers: FC<ControlRenderProps>[] =
347
- c.children?.map((cd) =>
348
- useControlRenderer(cd, controlDataContext.fields, renderer, {
349
- ...options,
350
- ...myOptions,
351
- }),
352
- ) ?? [];
350
+ const childOptions: ControlRenderOptions = { ...options, ...myOptions };
353
351
 
354
352
  useEffect(() => {
355
353
  if (control && typeof myOptions.disabled === "boolean")
@@ -364,15 +362,26 @@ export function useControlRenderer(
364
362
  const labelAndChildren = renderControlLayout({
365
363
  definition: c,
366
364
  renderer,
367
- childCount: childRenderers.length,
368
- renderChild: (k, i, props) => {
369
- const RenderChild = childRenderers[i];
370
- return <RenderChild key={k} {...props} />;
371
- },
365
+ renderChild: (k, child, path) => (
366
+ <ControlRenderer
367
+ key={k}
368
+ control={controlDataContext.data}
369
+ fields={controlDataContext.fields}
370
+ definition={findChildDefinition(c, child)}
371
+ parentPath={
372
+ path
373
+ ? [...controlDataContext.path, ...path]
374
+ : controlDataContext.path
375
+ }
376
+ renderer={renderer}
377
+ options={childOptions}
378
+ />
379
+ ),
372
380
  createDataProps: dataProps,
373
381
  formOptions: myOptions,
374
382
  dataContext: controlDataContext,
375
383
  control: displayControl ?? control,
384
+ labelText,
376
385
  schemaField,
377
386
  displayControl,
378
387
  style: customStyle,
@@ -399,6 +408,7 @@ export function useControlRenderer(
399
408
  useCustomStyle,
400
409
  useLayoutStyle,
401
410
  useAllowedOptions,
411
+ useLabelText,
402
412
  useDynamicDisplay,
403
413
  useValidation,
404
414
  renderer,
@@ -445,19 +455,36 @@ export function getControlData(
445
455
  ];
446
456
  }
447
457
 
458
+ export function ControlRenderer({
459
+ definition,
460
+ fields,
461
+ renderer,
462
+ options,
463
+ control,
464
+ parentPath,
465
+ }: {
466
+ definition: ControlDefinition;
467
+ fields: SchemaField[];
468
+ renderer: FormRenderer;
469
+ options?: ControlRenderOptions;
470
+ control: Control<any>;
471
+ parentPath?: JsonPath[];
472
+ }) {
473
+ const Render = useControlRenderer(definition, fields, renderer, options);
474
+ return <Render control={control} parentPath={parentPath} />;
475
+ }
476
+
448
477
  function groupProps(
449
- renderOptions: GroupRenderOptions = { type: "Standard" },
450
- childCount: number,
478
+ definition: GroupedControlsDefinition,
451
479
  renderChild: ChildRenderer,
452
480
  data: DataContext,
453
481
  className: string | null | undefined,
454
482
  style: React.CSSProperties | undefined,
455
483
  ): GroupRendererProps {
456
484
  return {
457
- childCount,
458
- renderChild: (i) =>
459
- renderChild(i, i, { control: data.data, parentPath: data.path }),
460
- renderOptions,
485
+ children: definition.children ?? [],
486
+ renderChild,
487
+ renderOptions: definition.groupOptions ?? { type: "Standard" },
461
488
  className: cc(className),
462
489
  style,
463
490
  };
@@ -479,7 +506,7 @@ export function defaultDataProps({
479
506
  (field.options?.length ?? 0) === 0 ? null : field.options;
480
507
  const allowed = allowedOptions?.value ?? [];
481
508
  return {
482
- definition,
509
+ children: definition.children ?? [],
483
510
  control,
484
511
  field,
485
512
  id: "c" + control.uniqueId,
@@ -541,19 +568,19 @@ export function defaultArrayProps(
541
568
 
542
569
  export type ChildRenderer = (
543
570
  k: Key,
544
- childIndex: number,
545
- props: ControlRenderProps,
571
+ child: number | number[],
572
+ parentPath?: JsonPath[],
546
573
  ) => ReactNode;
547
574
 
548
575
  export interface RenderControlProps {
549
576
  definition: ControlDefinition;
550
577
  renderer: FormRenderer;
551
- childCount: number;
552
578
  renderChild: ChildRenderer;
553
579
  createDataProps: CreateDataProps;
554
580
  formOptions: FormContextOptions;
555
581
  dataContext: ControlDataContext;
556
582
  control?: Control<any>;
583
+ labelText?: Control<string | null | undefined>;
557
584
  schemaField?: SchemaField;
558
585
  displayControl?: Control<string | undefined>;
559
586
  style?: React.CSSProperties;
@@ -562,7 +589,6 @@ export interface RenderControlProps {
562
589
  export function renderControlLayout({
563
590
  definition: c,
564
591
  renderer,
565
- childCount,
566
592
  renderChild: childRenderer,
567
593
  control: childControl,
568
594
  schemaField,
@@ -571,6 +597,7 @@ export function renderControlLayout({
571
597
  createDataProps: dataProps,
572
598
  displayControl,
573
599
  style,
600
+ labelText,
574
601
  allowedOptions,
575
602
  }: RenderControlProps): ControlLayoutProps {
576
603
  if (isDataControlDefinition(c)) {
@@ -587,17 +614,11 @@ export function renderControlLayout({
587
614
  }
588
615
  return {
589
616
  processLayout: renderer.renderGroup(
590
- groupProps(
591
- c.groupOptions,
592
- childCount,
593
- childRenderer,
594
- dataContext,
595
- c.styleClass,
596
- style,
597
- ),
617
+ groupProps(c, childRenderer, dataContext, c.styleClass, style),
598
618
  ),
599
619
  label: {
600
- label: c.title,
620
+ label: labelText?.value ?? c.title,
621
+ className: cc(c.labelClass),
601
622
  type: LabelType.Group,
602
623
  hide: c.groupOptions?.hideTitle,
603
624
  },
@@ -606,7 +627,7 @@ export function renderControlLayout({
606
627
  if (isActionControlsDefinition(c)) {
607
628
  return {
608
629
  children: renderer.renderAction({
609
- actionText: c.title ?? c.actionId,
630
+ actionText: labelText?.value ?? c.title ?? c.actionId,
610
631
  actionId: c.actionId,
611
632
  onClick: () => {},
612
633
  className: cc(c.styleClass),
@@ -640,26 +661,30 @@ export function renderControlLayout({
640
661
  elemIndex != null ? childControl!.elements[elemIndex] : childControl,
641
662
  options: dataOptions,
642
663
  style,
643
- childCount,
644
664
  allowedOptions,
645
- renderChild: childRenderer,
665
+ renderChild:
666
+ elemIndex != null
667
+ ? (k, d, p) =>
668
+ childRenderer(k, d, p ? [elemIndex, ...p] : [elemIndex])
669
+ : childRenderer,
646
670
  elementRenderer:
647
671
  elemIndex == null && schemaField.collection
648
672
  ? (ei) => renderLayoutParts(renderData(c, ei), renderer).children
649
673
  : undefined,
650
674
  });
651
675
 
652
- const labelText = !c.hideTitle
653
- ? controlTitle(c.title, schemaField)
676
+ const label = !c.hideTitle
677
+ ? controlTitle(labelText?.value ?? c.title, schemaField)
654
678
  : undefined;
655
679
  return {
656
680
  processLayout: renderer.renderData(props),
657
681
  label: {
658
682
  type: LabelType.Control,
659
- label: labelText,
683
+ label,
660
684
  forId: props.id,
661
685
  required: c.required,
662
686
  hide: c.hideTitle,
687
+ className: cc(c.labelClass),
663
688
  },
664
689
  errorControl: childControl,
665
690
  };
package/src/hooks.tsx CHANGED
@@ -306,15 +306,16 @@ export function useJsonataExpression(
306
306
  dataContext: DataContext,
307
307
  bindings?: () => Record<string, any>,
308
308
  ): Control<any> {
309
- const pathString = jsonPathString(dataContext.path);
309
+ const pathString = jsonPathString(dataContext.path, (x) => `#$i[${x}]`);
310
+ const fullExpr = pathString ? pathString + ".(" + jExpr + ")" : jExpr;
310
311
  const compiledExpr = useMemo(() => {
311
312
  try {
312
- return jsonata(pathString ? pathString + ".(" + jExpr + ")" : jExpr);
313
+ return jsonata(fullExpr);
313
314
  } catch (e) {
314
315
  console.error(e);
315
316
  return jsonata("null");
316
317
  }
317
- }, [jExpr, pathString]);
318
+ }, [fullExpr]);
318
319
  const control = useControl();
319
320
  const listenerRef = useRef<() => void>();
320
321
  const [ref] = useRefState(() =>
@@ -350,3 +351,23 @@ export function useJsonataExpression(
350
351
  }, [compiledExpr]);
351
352
  return control;
352
353
  }
354
+
355
+ export function useEvalLabelText(
356
+ useExpr: UseEvalExpressionHook,
357
+ definition: ControlDefinition,
358
+ ): EvalExpressionHook<string | null> {
359
+ const dynamicValue = useEvalDynamicHook(
360
+ definition,
361
+ DynamicPropertyType.Label,
362
+ useExpr,
363
+ );
364
+ return useCallback(
365
+ (ctx) => {
366
+ if (dynamicValue) {
367
+ return dynamicValue(ctx);
368
+ }
369
+ return useControl(null);
370
+ },
371
+ [dynamicValue],
372
+ );
373
+ }
package/src/renderers.tsx CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  AdornmentPlacement,
32
32
  ControlAdornment,
33
33
  ControlAdornmentType,
34
+ ControlDefinitionType,
34
35
  DataRenderType,
35
36
  DisplayDataType,
36
37
  FieldOption,
@@ -47,7 +48,7 @@ import {
47
48
  SchemaInterface,
48
49
  TextDisplay,
49
50
  } from "./types";
50
- import { hasOptions } from "./util";
51
+ import { getOverrideClass, hasOptions, rendererClass } from "./util";
51
52
 
52
53
  export interface DefaultRenderers {
53
54
  data: DataRendererRegistration;
@@ -264,6 +265,7 @@ export function createFormRenderer(
264
265
  interface DefaultLabelRendererOptions {
265
266
  className?: string;
266
267
  groupLabelClass?: string;
268
+ controlLabelClass?: string;
267
269
  requiredElement?: ReactNode;
268
270
  }
269
271
 
@@ -286,16 +288,21 @@ export function createDefaultActionRenderer(
286
288
  export function createDefaultLabelRenderer(
287
289
  options: DefaultLabelRendererOptions = { requiredElement: <span> *</span> },
288
290
  ): LabelRendererRegistration {
289
- const { className, groupLabelClass, requiredElement } = options;
291
+ const { className, groupLabelClass, controlLabelClass, requiredElement } =
292
+ options;
290
293
  return {
291
294
  render: (props, labelStart, labelEnd) => (
292
295
  <>
293
296
  {labelStart}
294
297
  <label
295
298
  htmlFor={props.forId}
296
- className={clsx(
297
- className,
298
- props.type === LabelType.Group && groupLabelClass,
299
+ className={rendererClass(
300
+ props.className,
301
+ clsx(
302
+ className,
303
+ props.type === LabelType.Group && groupLabelClass,
304
+ props.type === LabelType.Control && controlLabelClass,
305
+ ),
299
306
  )}
300
307
  >
301
308
  {props.label}
@@ -423,7 +430,7 @@ export function createDefaultGroupRenderer(
423
430
  }
424
431
 
425
432
  function render(props: GroupRendererProps) {
426
- const { childCount, renderChild, renderOptions } = props;
433
+ const { renderChild, renderOptions, children } = props;
427
434
 
428
435
  const { style, className: gcn } = isGridRenderer(renderOptions)
429
436
  ? gridStyles(renderOptions)
@@ -435,8 +442,11 @@ export function createDefaultGroupRenderer(
435
442
  return {
436
443
  ...cp,
437
444
  children: (
438
- <div className={clsx(props.className, className, gcn)} style={style}>
439
- {Array.from({ length: childCount }, (_, x) => renderChild(x))}
445
+ <div
446
+ className={rendererClass(props.className, clsx(className, gcn))}
447
+ style={style}
448
+ >
449
+ {children?.map((c, i) => renderChild(i, i))}
440
450
  </div>
441
451
  ),
442
452
  };
@@ -471,14 +481,17 @@ export function DefaultDisplay({
471
481
  <i
472
482
  style={style}
473
483
  className={clsx(
474
- className,
484
+ getOverrideClass(className),
475
485
  display ? display.value : (data as IconDisplay).iconClass,
476
486
  )}
477
487
  />
478
488
  );
479
489
  case DisplayDataType.Text:
480
490
  return (
481
- <div style={style} className={clsx(className, options.textClassName)}>
491
+ <div
492
+ style={style}
493
+ className={rendererClass(className, options.textClassName)}
494
+ >
482
495
  {display ? display.value : (data as TextDisplay).text}
483
496
  </div>
484
497
  );
@@ -486,7 +499,7 @@ export function DefaultDisplay({
486
499
  return (
487
500
  <div
488
501
  style={style}
489
- className={clsx(className, options.htmlClassName)}
502
+ className={rendererClass(className, options.htmlClassName)}
490
503
  dangerouslySetInnerHTML={{
491
504
  __html: display ? display.value ?? "" : (data as HtmlDisplay).html,
492
505
  }}
@@ -530,13 +543,9 @@ export function createDefaultDataRenderer(
530
543
  return renderers.renderGroup({
531
544
  style: props.style,
532
545
  className: props.className,
546
+ children: props.children,
533
547
  renderOptions: { type: "Standard", hideTitle: true },
534
- renderChild: (i) =>
535
- props.renderChild(i, i, {
536
- control: props.dataContext.data,
537
- parentPath: props.dataContext.path,
538
- }),
539
- childCount: props.childCount,
548
+ renderChild: props.renderChild,
540
549
  });
541
550
  }
542
551
  const renderOptions = props.renderOptions;
@@ -576,7 +585,7 @@ export function createDefaultDataRenderer(
576
585
  />
577
586
  ) : (
578
587
  <ControlInput
579
- className={clsx(props.className, inputClass)}
588
+ className={rendererClass(props.className, inputClass)}
580
589
  style={props.style}
581
590
  id={props.id}
582
591
  readOnly={props.readonly}
@@ -608,7 +617,7 @@ export function DefaultDisplayOnly({
608
617
  ? emptyText
609
618
  : schemaInterface.textValue(field, v)) ?? "";
610
619
  return (
611
- <div style={style} className={className}>
620
+ <div style={style} className={rendererClass(className)}>
612
621
  {text}
613
622
  </div>
614
623
  );
@@ -696,7 +705,10 @@ function createDefaultLayoutRenderer(
696
705
  ) {
697
706
  return createLayoutRenderer((props, renderers) => {
698
707
  const layout = renderLayoutParts(
699
- { ...props, className: clsx(props.className, options.className) },
708
+ {
709
+ ...props,
710
+ className: rendererClass(props.className, options.className),
711
+ },
700
712
  renderers,
701
713
  );
702
714
  return {
@@ -835,7 +847,7 @@ export function createSelectRenderer(options: SelectRendererOptions = {}) {
835
847
  return createDataRenderer(
836
848
  (props, asArray) => (
837
849
  <SelectDataRenderer
838
- className={clsx(props.className, options.className)}
850
+ className={rendererClass(props.className, options.className)}
839
851
  state={props.control}
840
852
  id={props.id}
841
853
  options={props.options!}
@@ -966,7 +978,6 @@ export function DefaultVisibility({
966
978
 
967
979
  export function DefaultLayout({
968
980
  errorClass,
969
- className,
970
981
  layout: { controlEnd, controlStart, label, children, errorControl },
971
982
  }: DefaultLayoutRendererOptions & {
972
983
  layout: RenderedLayout;
@@ -144,3 +144,28 @@ export function defaultCompoundField(
144
144
  children: [],
145
145
  };
146
146
  }
147
+
148
+ export function mergeField(
149
+ field: SchemaField,
150
+ mergeInto: SchemaField[],
151
+ ): SchemaField[] {
152
+ const existing = mergeInto.find((x) => x.field === field.field);
153
+ if (existing) {
154
+ return mergeInto.map((x) =>
155
+ x !== existing
156
+ ? x
157
+ : {
158
+ ...x,
159
+ onlyForTypes: mergeTypes(x.onlyForTypes, field.onlyForTypes),
160
+ },
161
+ );
162
+ }
163
+ return [...mergeInto, field];
164
+
165
+ function mergeTypes(f?: string[] | null, s?: string[] | null) {
166
+ if (!f) return s;
167
+ if (!s) return f;
168
+ const extras = s.filter((x) => !f.includes(x));
169
+ return extras.length ? [...f, ...extras] : f;
170
+ }
171
+ }