@react-typed-forms/schemas 5.0.2 → 6.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.
@@ -21,6 +21,7 @@ import {
21
21
  ControlDefinition,
22
22
  DataControlDefinition,
23
23
  DisplayData,
24
+ DynamicPropertyType,
24
25
  FieldOption,
25
26
  GroupRenderOptions,
26
27
  isActionControlsDefinition,
@@ -29,9 +30,10 @@ import {
29
30
  isGroupControlsDefinition,
30
31
  RenderOptions,
31
32
  SchemaField,
33
+ SchemaInterface,
32
34
  } from "./types";
33
35
  import {
34
- ControlGroupContext,
36
+ ControlDataContext,
35
37
  elementValueForField,
36
38
  fieldDisplayName,
37
39
  findField,
@@ -43,19 +45,24 @@ import {
43
45
  defaultUseEvalExpressionHook,
44
46
  useEvalDefaultValueHook,
45
47
  useEvalDisabledHook,
48
+ useEvalDisplayHook,
46
49
  UseEvalExpressionHook,
47
50
  useEvalReadonlyHook,
51
+ useEvalStyleHook,
48
52
  useEvalVisibilityHook,
49
53
  } from "./hooks";
50
54
  import { useValidationHook } from "./validators";
51
- import { useCalculatedControl } from "./internal";
55
+ import { cc, useCalculatedControl } from "./internal";
56
+ import { defaultSchemaInterface } from "./schemaInterface";
52
57
 
53
58
  export interface FormRenderer {
54
59
  renderData: (
55
60
  props: DataRendererProps,
56
61
  asArray: (() => ReactNode) | undefined,
57
62
  ) => (layout: ControlLayoutProps) => ControlLayoutProps;
58
- renderGroup: (props: GroupRendererProps) => ReactNode;
63
+ renderGroup: (
64
+ props: GroupRendererProps,
65
+ ) => (layout: ControlLayoutProps) => ControlLayoutProps;
59
66
  renderDisplay: (props: DisplayRendererProps) => ReactNode;
60
67
  renderAction: (props: ActionRendererProps) => ReactNode;
61
68
  renderArray: (props: ArrayRendererProps) => ReactNode;
@@ -65,16 +72,10 @@ export interface FormRenderer {
65
72
  labelStart: ReactNode,
66
73
  labelEnd: ReactNode,
67
74
  ) => ReactNode;
68
- renderLayout: (props: ControlLayoutProps) => ReactNode;
69
- renderVisibility: (
70
- control: Control<Visibility | undefined>,
71
- children: () => ReactNode,
72
- ) => ReactNode;
75
+ renderLayout: (props: ControlLayoutProps) => RenderedControl;
76
+ renderVisibility: (props: VisibilityRendererProps) => ReactNode;
73
77
  }
74
78
 
75
- export interface DisplayRendererProps {
76
- data: DisplayData;
77
- }
78
79
  export interface AdornmentProps {
79
80
  adornment: ControlAdornment;
80
81
  }
@@ -96,6 +97,8 @@ export interface ArrayRendererProps {
96
97
  renderChild: (childIndex: number) => ReactNode;
97
98
  childKey: (childIndex: number) => Key;
98
99
  arrayControl?: Control<any[] | undefined | null>;
100
+ className?: string;
101
+ style?: React.CSSProperties;
99
102
  }
100
103
  export interface Visibility {
101
104
  visible: boolean;
@@ -110,6 +113,19 @@ export interface RenderedLayout {
110
113
  label?: ReactNode;
111
114
  children?: ReactNode;
112
115
  errorControl?: Control<any>;
116
+ className?: string;
117
+ style?: React.CSSProperties;
118
+ }
119
+
120
+ export interface RenderedControl {
121
+ children: ReactNode;
122
+ className?: string;
123
+ style?: React.CSSProperties;
124
+ divRef?: (cb: HTMLElement | null) => void;
125
+ }
126
+
127
+ export interface VisibilityRendererProps extends RenderedControl {
128
+ visibility: Control<Visibility | undefined>;
113
129
  }
114
130
 
115
131
  export interface ControlLayoutProps {
@@ -118,6 +134,8 @@ export interface ControlLayoutProps {
118
134
  adornments?: AdornmentRenderer[];
119
135
  children?: ReactNode;
120
136
  processLayout?: (props: ControlLayoutProps) => ControlLayoutProps;
137
+ className?: string | null;
138
+ style?: React.CSSProperties;
121
139
  }
122
140
 
123
141
  export enum LabelType {
@@ -131,10 +149,19 @@ export interface LabelRendererProps {
131
149
  required?: boolean | null;
132
150
  forId?: string;
133
151
  }
152
+ export interface DisplayRendererProps {
153
+ data: DisplayData;
154
+ display?: Control<string | undefined>;
155
+ className?: string;
156
+ style?: React.CSSProperties;
157
+ }
158
+
134
159
  export interface GroupRendererProps {
135
160
  renderOptions: GroupRenderOptions;
136
161
  childCount: number;
137
162
  renderChild: (child: number) => ReactNode;
163
+ className?: string;
164
+ style?: React.CSSProperties;
138
165
  }
139
166
 
140
167
  export interface DataRendererProps {
@@ -146,12 +173,17 @@ export interface DataRendererProps {
146
173
  required: boolean;
147
174
  options: FieldOption[] | undefined | null;
148
175
  hidden: boolean;
176
+ className?: string;
177
+ style?: React.CSSProperties;
178
+ dataContext: ControlDataContext;
149
179
  }
150
180
 
151
181
  export interface ActionRendererProps {
152
182
  actionId: string;
153
183
  actionText: string;
154
184
  onClick: () => void;
185
+ className?: string;
186
+ style?: React.CSSProperties;
155
187
  }
156
188
 
157
189
  export interface ControlRenderProps {
@@ -164,17 +196,23 @@ export interface FormContextOptions {
164
196
  disabled?: boolean | null;
165
197
  }
166
198
 
199
+ export interface DataControlProps {
200
+ definition: DataControlDefinition;
201
+ field: SchemaField;
202
+ dataContext: ControlDataContext;
203
+ control: Control<any>;
204
+ options: FormContextOptions;
205
+ style: React.CSSProperties | undefined;
206
+ }
167
207
  export type CreateDataProps = (
168
- definition: DataControlDefinition,
169
- field: SchemaField,
170
- groupContext: ControlGroupContext,
171
- control: Control<any>,
172
- options: FormContextOptions,
208
+ controlProps: DataControlProps,
173
209
  ) => DataRendererProps;
210
+
174
211
  export interface ControlRenderOptions extends FormContextOptions {
175
212
  useDataHook?: (c: ControlDefinition) => CreateDataProps;
176
213
  useEvalExpressionHook?: UseEvalExpressionHook;
177
214
  clearHidden?: boolean;
215
+ schemaInterface?: SchemaInterface;
178
216
  }
179
217
  export function useControlRenderer(
180
218
  definition: ControlDefinition,
@@ -183,6 +221,7 @@ export function useControlRenderer(
183
221
  options: ControlRenderOptions = {},
184
222
  ): FC<ControlRenderProps> {
185
223
  const dataProps = options.useDataHook?.(definition) ?? defaultDataProps;
224
+ const schemaInterface = options.schemaInterface ?? defaultSchemaInterface;
186
225
  const useExpr = options.useEvalExpressionHook ?? defaultUseEvalExpressionHook;
187
226
 
188
227
  const schemaField = lookupSchemaField(definition, fields);
@@ -194,6 +233,17 @@ export function useControlRenderer(
194
233
  const useIsVisible = useEvalVisibilityHook(useExpr, definition, schemaField);
195
234
  const useIsReadonly = useEvalReadonlyHook(useExpr, definition);
196
235
  const useIsDisabled = useEvalDisabledHook(useExpr, definition);
236
+ const useCustomStyle = useEvalStyleHook(
237
+ useExpr,
238
+ DynamicPropertyType.Style,
239
+ definition,
240
+ );
241
+ const useLayoutStyle = useEvalStyleHook(
242
+ useExpr,
243
+ DynamicPropertyType.LayoutStyle,
244
+ definition,
245
+ );
246
+ const useDynamicDisplay = useEvalDisplayHook(useExpr, definition);
197
247
  const useValidation = useValidationHook(definition);
198
248
  const r = useUpdatedRef({ options, definition, fields, schemaField });
199
249
 
@@ -202,13 +252,17 @@ export function useControlRenderer(
202
252
  const stopTracking = useComponentTracking();
203
253
  try {
204
254
  const { definition: c, options, fields, schemaField } = r.current;
205
- const groupContext: ControlGroupContext = {
255
+ const dataContext: ControlDataContext = {
206
256
  groupControl: parentControl,
207
257
  fields,
258
+ schemaInterface,
208
259
  };
209
- const readonlyControl = useIsReadonly(groupContext);
210
- const disabledControl = useIsDisabled(groupContext);
211
- const visibleControl = useIsVisible(groupContext);
260
+ const readonlyControl = useIsReadonly(dataContext);
261
+ const disabledControl = useIsDisabled(dataContext);
262
+ const visibleControl = useIsVisible(dataContext);
263
+ const displayControl = useDynamicDisplay(dataContext);
264
+ const customStyle = useCustomStyle(dataContext).value;
265
+ const layoutStyle = useLayoutStyle(dataContext).value;
212
266
  const visible = visibleControl.current.value;
213
267
  const visibility = useControl<Visibility | undefined>(() =>
214
268
  visible != null
@@ -229,10 +283,10 @@ export function useControlRenderer(
229
283
  },
230
284
  );
231
285
 
232
- const defaultValueControl = useDefaultValue(groupContext);
286
+ const defaultValueControl = useDefaultValue(dataContext);
233
287
  const [control, childContext] = getControlData(
234
288
  schemaField,
235
- groupContext,
289
+ dataContext,
236
290
  );
237
291
  useControlEffect(
238
292
  () => [
@@ -260,7 +314,7 @@ export function useControlRenderer(
260
314
  readonly: options.readonly || readonlyControl.value,
261
315
  disabled: options.disabled || disabledControl.value,
262
316
  })).value;
263
- useValidation(control!, !!myOptions.hidden, groupContext);
317
+ useValidation(control!, !!myOptions.hidden, dataContext);
264
318
  const childRenderers: FC<ControlRenderProps>[] =
265
319
  c.children?.map((cd) =>
266
320
  useControlRenderer(cd, childContext.fields, renderer, {
@@ -277,23 +331,29 @@ export function useControlRenderer(
277
331
  definition.adornments?.map((x) =>
278
332
  renderer.renderAdornment({ adornment: x }),
279
333
  ) ?? [];
280
- const labelAndChildren = renderControlLayout(
281
- c,
334
+ const labelAndChildren = renderControlLayout({
335
+ definition: c,
282
336
  renderer,
283
- childRenderers.length,
284
- (k, i, props) => {
337
+ childCount: childRenderers.length,
338
+ renderChild: (k, i, props) => {
285
339
  const RenderChild = childRenderers[i];
286
340
  return <RenderChild key={k} {...props} />;
287
341
  },
288
- dataProps,
289
- myOptions,
290
- groupContext,
291
- control,
342
+ createDataProps: dataProps,
343
+ formOptions: myOptions,
344
+ dataContext,
345
+ control: displayControl ?? control,
292
346
  schemaField,
293
- );
294
- return renderer.renderVisibility(visibility, () =>
295
- renderer.renderLayout({ ...labelAndChildren, adornments }),
296
- );
347
+ displayControl,
348
+ style: customStyle,
349
+ });
350
+ const renderedControl = renderer.renderLayout({
351
+ ...labelAndChildren,
352
+ adornments,
353
+ className: c.layoutClass,
354
+ style: layoutStyle,
355
+ });
356
+ return renderer.renderVisibility({ visibility, ...renderedControl });
297
357
  } finally {
298
358
  stopTracking();
299
359
  }
@@ -305,8 +365,12 @@ export function useControlRenderer(
305
365
  useDefaultValue,
306
366
  useIsReadonly,
307
367
  useIsDisabled,
368
+ useCustomStyle,
369
+ useLayoutStyle,
370
+ useDynamicDisplay,
308
371
  useValidation,
309
372
  renderer,
373
+ schemaInterface,
310
374
  ],
311
375
  );
312
376
  (Component as any).displayName = "RenderControl";
@@ -319,14 +383,14 @@ export function lookupSchemaField(
319
383
  const fieldName = isGroupControlsDefinition(c)
320
384
  ? c.compoundField
321
385
  : isDataControlDefinition(c)
322
- ? c.field
323
- : undefined;
386
+ ? c.field
387
+ : undefined;
324
388
  return fieldName ? findField(fields, fieldName) : undefined;
325
389
  }
326
390
  export function getControlData(
327
391
  schemaField: SchemaField | undefined,
328
- parentContext: ControlGroupContext,
329
- ): [Control<any> | undefined, ControlGroupContext] {
392
+ parentContext: ControlDataContext,
393
+ ): [Control<any> | undefined, ControlDataContext] {
330
394
  const childControl: Control<any> | undefined = schemaField
331
395
  ? parentContext.groupControl.fields?.[schemaField.field] ?? newControl({})
332
396
  : undefined;
@@ -336,6 +400,7 @@ export function getControlData(
336
400
  ? {
337
401
  groupControl: childControl!,
338
402
  fields: schemaField.children,
403
+ schemaInterface: parentContext.schemaInterface,
339
404
  }
340
405
  : parentContext,
341
406
  ];
@@ -348,6 +413,8 @@ function renderArray(
348
413
  required: boolean,
349
414
  arrayControl: Control<any[] | undefined | null>,
350
415
  renderChild: (elemIndex: number, control: Control<any>) => ReactNode,
416
+ className: string | null | undefined,
417
+ style: React.CSSProperties | undefined,
351
418
  ) {
352
419
  const elems = arrayControl.elements ?? [];
353
420
  return renderer.renderArray({
@@ -366,6 +433,8 @@ function renderArray(
366
433
  onClick: () => removeElement(arrayControl, i),
367
434
  }),
368
435
  renderChild: (i) => renderChild(i, elems[i]),
436
+ className: cc(className),
437
+ style,
369
438
  });
370
439
  }
371
440
  function groupProps(
@@ -373,21 +442,26 @@ function groupProps(
373
442
  childCount: number,
374
443
  renderChild: ChildRenderer,
375
444
  control: Control<any>,
445
+ className: string | null | undefined,
446
+ style: React.CSSProperties | undefined,
376
447
  ): GroupRendererProps {
377
448
  return {
378
449
  childCount,
379
450
  renderChild: (i) => renderChild(i, i, { control }),
380
451
  renderOptions,
452
+ className: cc(className),
453
+ style,
381
454
  };
382
455
  }
383
456
 
384
- export const defaultDataProps: CreateDataProps = (
457
+ export function defaultDataProps({
385
458
  definition,
386
459
  field,
387
- groupContext,
460
+ dataContext,
388
461
  control,
389
462
  options,
390
- ) => {
463
+ style,
464
+ }: DataControlProps): DataRendererProps {
391
465
  return {
392
466
  control,
393
467
  field,
@@ -397,25 +471,44 @@ export const defaultDataProps: CreateDataProps = (
397
471
  renderOptions: definition.renderOptions ?? { type: "Standard" },
398
472
  required: !!definition.required,
399
473
  hidden: !!options.hidden,
474
+ className: cc(definition.styleClass),
475
+ style,
476
+ dataContext,
400
477
  };
401
- };
478
+ }
402
479
 
403
480
  export type ChildRenderer = (
404
481
  k: Key,
405
482
  childIndex: number,
406
483
  props: ControlRenderProps,
407
484
  ) => ReactNode;
408
- export function renderControlLayout(
409
- c: ControlDefinition,
410
- renderer: FormRenderer,
411
- childCount: number,
412
- childRenderer: ChildRenderer,
413
- dataProps: CreateDataProps,
414
- dataOptions: FormContextOptions,
415
- groupContext: ControlGroupContext,
416
- childControl?: Control<any>,
417
- schemaField?: SchemaField,
418
- ): ControlLayoutProps {
485
+
486
+ export interface RenderControlProps {
487
+ definition: ControlDefinition;
488
+ renderer: FormRenderer;
489
+ childCount: number;
490
+ renderChild: ChildRenderer;
491
+ createDataProps: CreateDataProps;
492
+ formOptions: FormContextOptions;
493
+ dataContext: ControlDataContext;
494
+ control?: Control<any>;
495
+ schemaField?: SchemaField;
496
+ displayControl?: Control<string | undefined>;
497
+ style?: React.CSSProperties;
498
+ }
499
+ export function renderControlLayout({
500
+ definition: c,
501
+ renderer,
502
+ childCount,
503
+ renderChild: childRenderer,
504
+ control: childControl,
505
+ schemaField,
506
+ dataContext,
507
+ formOptions: dataOptions,
508
+ createDataProps: dataProps,
509
+ displayControl,
510
+ style,
511
+ }: RenderControlProps): ControlLayoutProps {
419
512
  if (isDataControlDefinition(c)) {
420
513
  return renderData(c);
421
514
  }
@@ -429,12 +522,14 @@ export function renderControlLayout(
429
522
  );
430
523
  }
431
524
  return {
432
- children: renderer.renderGroup(
525
+ processLayout: renderer.renderGroup(
433
526
  groupProps(
434
527
  c.groupOptions,
435
528
  childCount,
436
529
  childRenderer,
437
- groupContext.groupControl,
530
+ dataContext.groupControl,
531
+ c.styleClass,
532
+ style,
438
533
  ),
439
534
  ),
440
535
  label: {
@@ -450,11 +545,20 @@ export function renderControlLayout(
450
545
  actionText: c.title ?? c.actionId,
451
546
  actionId: c.actionId,
452
547
  onClick: () => {},
548
+ className: cc(c.styleClass),
549
+ style,
453
550
  }),
454
551
  };
455
552
  }
456
553
  if (isDisplayControlsDefinition(c)) {
457
- return { children: renderer.renderDisplay({ data: c.displayData ?? {} }) };
554
+ return {
555
+ children: renderer.renderDisplay({
556
+ data: c.displayData ?? {},
557
+ className: cc(c.styleClass),
558
+ style,
559
+ display: displayControl,
560
+ }),
561
+ };
458
562
  }
459
563
  return {};
460
564
 
@@ -477,30 +581,35 @@ export function renderControlLayout(
477
581
  !!c.required,
478
582
  childControl!,
479
583
  compoundRenderer,
584
+ c.styleClass,
585
+ style,
480
586
  ),
481
587
  errorControl: childControl,
482
588
  };
483
589
  }
484
590
  return {
485
- children: renderer.renderGroup(
591
+ processLayout: renderer.renderGroup(
486
592
  groupProps(
487
593
  { type: "Standard" },
488
594
  childCount,
489
595
  childRenderer,
490
596
  childControl!,
597
+ c.styleClass,
598
+ style,
491
599
  ),
492
600
  ),
493
601
  label,
494
602
  errorControl: childControl,
495
603
  };
496
604
  }
497
- const props = dataProps(
498
- c,
499
- schemaField,
500
- groupContext,
501
- childControl!,
502
- dataOptions,
503
- );
605
+ const props = dataProps({
606
+ definition: c,
607
+ field: schemaField,
608
+ dataContext,
609
+ control: childControl!,
610
+ options: dataOptions,
611
+ style,
612
+ });
504
613
  const labelText = !c.hideTitle
505
614
  ? controlTitle(c.title, schemaField)
506
615
  : undefined;
@@ -516,6 +625,8 @@ export function renderControlLayout(
516
625
  !!c.required,
517
626
  childControl!,
518
627
  scalarRenderer(props),
628
+ c.styleClass,
629
+ style,
519
630
  )
520
631
  : undefined,
521
632
  ),
@@ -531,14 +642,17 @@ export function renderControlLayout(
531
642
  }
532
643
 
533
644
  function compoundRenderer(i: number, control: Control<any>): ReactNode {
645
+ const { className, style, children } = renderer.renderLayout({
646
+ processLayout: renderer.renderGroup({
647
+ renderOptions: { type: "Standard", hideTitle: true },
648
+ childCount,
649
+ renderChild: (ci) => childRenderer(ci, ci, { control }),
650
+ }),
651
+ });
534
652
  return (
535
- <Fragment key={control.uniqueId}>
536
- {renderer.renderGroup({
537
- renderOptions: { type: "Standard", hideTitle: true },
538
- childCount,
539
- renderChild: (ci) => childRenderer(ci, ci, { control }),
540
- })}
541
- </Fragment>
653
+ <div key={control.uniqueId} style={style} className={cc(className)}>
654
+ {children}
655
+ </div>
542
656
  );
543
657
  }
544
658
  function scalarRenderer(
@@ -558,7 +672,7 @@ export function renderControlLayout(
558
672
  }
559
673
 
560
674
  export function appendMarkup(
561
- k: keyof Omit<RenderedLayout, "errorControl">,
675
+ k: keyof Omit<RenderedLayout, "errorControl" | "style" | "className">,
562
676
  markup: ReactNode,
563
677
  ): (layout: RenderedLayout) => void {
564
678
  return (layout) =>
@@ -571,7 +685,7 @@ export function appendMarkup(
571
685
  }
572
686
 
573
687
  export function wrapMarkup(
574
- k: keyof Omit<RenderedLayout, "errorControl">,
688
+ k: keyof Omit<RenderedLayout, "errorControl" | "style" | "className">,
575
689
  wrap: (ex: ReactNode) => ReactNode,
576
690
  ): (layout: RenderedLayout) => void {
577
691
  return (layout) => (layout[k] = wrap(layout[k]));
@@ -579,7 +693,7 @@ export function wrapMarkup(
579
693
 
580
694
  export function layoutKeyForPlacement(
581
695
  pos: AdornmentPlacement,
582
- ): keyof Omit<RenderedLayout, "errorControl"> {
696
+ ): keyof Omit<RenderedLayout, "errorControl" | "style" | "className"> {
583
697
  switch (pos) {
584
698
  case AdornmentPlacement.ControlEnd:
585
699
  return "controlEnd";
@@ -610,18 +724,20 @@ export function renderLayoutParts(
610
724
  props: ControlLayoutProps,
611
725
  renderer: FormRenderer,
612
726
  ): RenderedLayout {
613
- const processed = props.processLayout?.(props) ?? props;
727
+ const { className, children, style, errorControl, label, adornments } =
728
+ props.processLayout?.(props) ?? props;
614
729
  const layout: RenderedLayout = {
615
- children: processed.children,
616
- errorControl: processed.errorControl,
730
+ children,
731
+ errorControl,
732
+ style,
733
+ className: cc(className),
617
734
  };
618
- (processed.adornments ?? [])
735
+ (adornments ?? [])
619
736
  .sort((a, b) => a.priority - b.priority)
620
737
  .forEach((x) => x.apply(layout));
621
- const l = processed.label;
622
738
  layout.label =
623
- l && !l.hide
624
- ? renderer.renderLabel(l, layout.labelStart, layout.labelEnd)
739
+ label && !label.hide
740
+ ? renderer.renderLabel(label, layout.labelStart, layout.labelEnd)
625
741
  : undefined;
626
742
  return layout;
627
743
  }