@react-typed-forms/schemas 7.3.2 → 8.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.
@@ -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/util.d.ts CHANGED
@@ -39,3 +39,4 @@ export declare function cleanDataForSchema(v: {
39
39
  } | undefined, fields: SchemaField[]): any;
40
40
  export declare function getAllReferencedClasses(c: ControlDefinition): string[];
41
41
  export declare function jsonPathString(jsonPath: JsonPath[]): string;
42
+ export declare function findChildDefinition(parent: ControlDefinition, childPath: number | number[]): ControlDefinition;
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.0.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,
@@ -160,15 +162,14 @@ export interface DisplayRendererProps {
160
162
  }
161
163
 
162
164
  export interface GroupRendererProps {
165
+ children: ControlDefinition[];
163
166
  renderOptions: GroupRenderOptions;
164
- childCount: number;
165
- renderChild: (child: number) => ReactNode;
167
+ renderChild: ChildRenderer;
166
168
  className?: string;
167
169
  style?: React.CSSProperties;
168
170
  }
169
171
 
170
172
  export interface DataRendererProps {
171
- definition: DataControlDefinition;
172
173
  renderOptions: RenderOptions;
173
174
  field: SchemaField;
174
175
  id: string;
@@ -180,7 +181,7 @@ export interface DataRendererProps {
180
181
  className?: string;
181
182
  style?: React.CSSProperties;
182
183
  dataContext: ControlDataContext;
183
- childCount: number;
184
+ children: ControlDefinition[];
184
185
  renderChild: ChildRenderer;
185
186
  toArrayProps?: () => ArrayRendererProps;
186
187
  }
@@ -211,7 +212,6 @@ export interface DataControlProps {
211
212
  control: Control<any>;
212
213
  options: FormContextOptions;
213
214
  style: React.CSSProperties | undefined;
214
- childCount: number;
215
215
  renderChild: ChildRenderer;
216
216
  allowedOptions?: Control<any[] | undefined>;
217
217
  elementRenderer?: (elemIndex: number) => ReactNode;
@@ -343,13 +343,7 @@ export function useControlRenderer(
343
343
  !!myOptions.hidden,
344
344
  parentDataContext,
345
345
  );
346
- const childRenderers: FC<ControlRenderProps>[] =
347
- c.children?.map((cd) =>
348
- useControlRenderer(cd, controlDataContext.fields, renderer, {
349
- ...options,
350
- ...myOptions,
351
- }),
352
- ) ?? [];
346
+ const childOptions: ControlRenderOptions = { ...options, ...myOptions };
353
347
 
354
348
  useEffect(() => {
355
349
  if (control && typeof myOptions.disabled === "boolean")
@@ -364,11 +358,21 @@ export function useControlRenderer(
364
358
  const labelAndChildren = renderControlLayout({
365
359
  definition: c,
366
360
  renderer,
367
- childCount: childRenderers.length,
368
- renderChild: (k, i, props) => {
369
- const RenderChild = childRenderers[i];
370
- return <RenderChild key={k} {...props} />;
371
- },
361
+ renderChild: (k, child, path) => (
362
+ <ControlRenderer
363
+ key={k}
364
+ control={controlDataContext.data}
365
+ fields={controlDataContext.fields}
366
+ definition={findChildDefinition(c, child)}
367
+ parentPath={
368
+ path
369
+ ? [...controlDataContext.path, ...path]
370
+ : controlDataContext.path
371
+ }
372
+ renderer={renderer}
373
+ options={childOptions}
374
+ />
375
+ ),
372
376
  createDataProps: dataProps,
373
377
  formOptions: myOptions,
374
378
  dataContext: controlDataContext,
@@ -445,19 +449,36 @@ export function getControlData(
445
449
  ];
446
450
  }
447
451
 
452
+ export function ControlRenderer({
453
+ definition,
454
+ fields,
455
+ renderer,
456
+ options,
457
+ control,
458
+ parentPath,
459
+ }: {
460
+ definition: ControlDefinition;
461
+ fields: SchemaField[];
462
+ renderer: FormRenderer;
463
+ options?: ControlRenderOptions;
464
+ control: Control<any>;
465
+ parentPath?: JsonPath[];
466
+ }) {
467
+ const Render = useControlRenderer(definition, fields, renderer, options);
468
+ return <Render control={control} parentPath={parentPath} />;
469
+ }
470
+
448
471
  function groupProps(
449
- renderOptions: GroupRenderOptions = { type: "Standard" },
450
- childCount: number,
472
+ definition: GroupedControlsDefinition,
451
473
  renderChild: ChildRenderer,
452
474
  data: DataContext,
453
475
  className: string | null | undefined,
454
476
  style: React.CSSProperties | undefined,
455
477
  ): GroupRendererProps {
456
478
  return {
457
- childCount,
458
- renderChild: (i) =>
459
- renderChild(i, i, { control: data.data, parentPath: data.path }),
460
- renderOptions,
479
+ children: definition.children ?? [],
480
+ renderChild,
481
+ renderOptions: definition.groupOptions ?? { type: "Standard" },
461
482
  className: cc(className),
462
483
  style,
463
484
  };
@@ -479,7 +500,7 @@ export function defaultDataProps({
479
500
  (field.options?.length ?? 0) === 0 ? null : field.options;
480
501
  const allowed = allowedOptions?.value ?? [];
481
502
  return {
482
- definition,
503
+ children: definition.children ?? [],
483
504
  control,
484
505
  field,
485
506
  id: "c" + control.uniqueId,
@@ -541,14 +562,13 @@ export function defaultArrayProps(
541
562
 
542
563
  export type ChildRenderer = (
543
564
  k: Key,
544
- childIndex: number,
545
- props: ControlRenderProps,
565
+ child: number | number[],
566
+ parentPath?: JsonPath[],
546
567
  ) => ReactNode;
547
568
 
548
569
  export interface RenderControlProps {
549
570
  definition: ControlDefinition;
550
571
  renderer: FormRenderer;
551
- childCount: number;
552
572
  renderChild: ChildRenderer;
553
573
  createDataProps: CreateDataProps;
554
574
  formOptions: FormContextOptions;
@@ -562,7 +582,6 @@ export interface RenderControlProps {
562
582
  export function renderControlLayout({
563
583
  definition: c,
564
584
  renderer,
565
- childCount,
566
585
  renderChild: childRenderer,
567
586
  control: childControl,
568
587
  schemaField,
@@ -587,14 +606,7 @@ export function renderControlLayout({
587
606
  }
588
607
  return {
589
608
  processLayout: renderer.renderGroup(
590
- groupProps(
591
- c.groupOptions,
592
- childCount,
593
- childRenderer,
594
- dataContext,
595
- c.styleClass,
596
- style,
597
- ),
609
+ groupProps(c, childRenderer, dataContext, c.styleClass, style),
598
610
  ),
599
611
  label: {
600
612
  label: c.title,
@@ -640,9 +652,12 @@ export function renderControlLayout({
640
652
  elemIndex != null ? childControl!.elements[elemIndex] : childControl,
641
653
  options: dataOptions,
642
654
  style,
643
- childCount,
644
655
  allowedOptions,
645
- renderChild: childRenderer,
656
+ renderChild:
657
+ elemIndex != null
658
+ ? (k, d, p) =>
659
+ childRenderer(k, d, p ? [elemIndex, ...p] : [elemIndex])
660
+ : childRenderer,
646
661
  elementRenderer:
647
662
  elemIndex == null && schemaField.collection
648
663
  ? (ei) => renderLayoutParts(renderData(c, ei), renderer).children
package/src/hooks.tsx CHANGED
@@ -307,14 +307,15 @@ export function useJsonataExpression(
307
307
  bindings?: () => Record<string, any>,
308
308
  ): Control<any> {
309
309
  const pathString = jsonPathString(dataContext.path);
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(() =>
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,
@@ -423,7 +424,7 @@ export function createDefaultGroupRenderer(
423
424
  }
424
425
 
425
426
  function render(props: GroupRendererProps) {
426
- const { childCount, renderChild, renderOptions } = props;
427
+ const { renderChild, renderOptions, children } = props;
427
428
 
428
429
  const { style, className: gcn } = isGridRenderer(renderOptions)
429
430
  ? gridStyles(renderOptions)
@@ -436,7 +437,7 @@ export function createDefaultGroupRenderer(
436
437
  ...cp,
437
438
  children: (
438
439
  <div className={clsx(props.className, className, gcn)} style={style}>
439
- {Array.from({ length: childCount }, (_, x) => renderChild(x))}
440
+ {children?.map((c, i) => renderChild(i, i))}
440
441
  </div>
441
442
  ),
442
443
  };
@@ -530,13 +531,9 @@ export function createDefaultDataRenderer(
530
531
  return renderers.renderGroup({
531
532
  style: props.style,
532
533
  className: props.className,
534
+ children: props.children,
533
535
  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,
536
+ renderChild: props.renderChild,
540
537
  });
541
538
  }
542
539
  const renderOptions = props.renderOptions;
@@ -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
+ }
package/src/util.ts CHANGED
@@ -408,3 +408,15 @@ export function jsonPathString(jsonPath: JsonPath[]) {
408
408
  });
409
409
  return out;
410
410
  }
411
+
412
+ export function findChildDefinition(
413
+ parent: ControlDefinition,
414
+ childPath: number | number[],
415
+ ): ControlDefinition {
416
+ if (Array.isArray(childPath)) {
417
+ let base = parent;
418
+ childPath.forEach((x) => (base = base.children![x]));
419
+ return base;
420
+ }
421
+ return parent.children![childPath];
422
+ }