@react-typed-forms/schemas 14.3.0 → 14.4.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.
@@ -11,6 +11,8 @@ export interface DefaultRenderers {
11
11
  adornment: AdornmentRendererRegistration;
12
12
  renderLayout: LayoutRendererRegistration;
13
13
  visibility: VisibilityRendererRegistration;
14
+ renderText: (props: ReactNode) => ReactNode;
15
+ h: (type: any, props: any, ...children: any[]) => ReactElement;
14
16
  }
15
17
  export interface LayoutRendererRegistration {
16
18
  type: "layout";
@@ -57,7 +59,7 @@ export interface AdornmentRendererRegistration {
57
59
  }
58
60
  export interface VisibilityRendererRegistration {
59
61
  type: "visibility";
60
- render: (props: VisibilityRendererProps) => ReactNode;
62
+ render: (props: VisibilityRendererProps, renderer: FormRenderer) => ReactNode;
61
63
  }
62
64
  export type RendererRegistration = DataRendererRegistration | GroupRendererRegistration | DisplayRendererRegistration | ActionRendererRegistration | LabelRendererRegistration | ArrayRendererRegistration | AdornmentRendererRegistration | LayoutRendererRegistration | VisibilityRendererRegistration;
63
65
  export declare function isIconAdornment(a: ControlAdornment): a is IconAdornment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-typed-forms/schemas",
3
- "version": "14.3.0",
3
+ "version": "14.4.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -356,8 +356,9 @@ export interface GridRenderer extends GroupRenderOptions {
356
356
  columns?: number | null;
357
357
  }
358
358
 
359
- export interface TabsGroupRenderer extends GroupRenderOptions {
359
+ export interface TabsRenderOptions extends GroupRenderOptions {
360
360
  type: GroupRenderType.Tabs;
361
+ contentClass?: string;
361
362
  }
362
363
 
363
364
  export interface SelectChildRenderer extends GroupRenderOptions {
@@ -444,7 +445,7 @@ export function isSelectChildRenderer(
444
445
 
445
446
  export function isTabsRenderer(
446
447
  options: GroupRenderOptions,
447
- ): options is TabsGroupRenderer {
448
+ ): options is TabsRenderOptions {
448
449
  return options.type === GroupRenderType.Tabs;
449
450
  }
450
451
 
@@ -545,63 +546,59 @@ export function isCheckEntryClasses(
545
546
 
546
547
  export type ControlMap = { [k: string]: ControlDefinition };
547
548
 
548
- export class FormNode {
549
- constructor(
550
- public id: string,
551
- public definition: ControlDefinition,
552
- public tree: FormTree,
553
- public parent?: FormNode,
554
- public overrideChildren?: FormNode[],
555
- ) {
556
- this.id = id;
557
- this.definition = definition;
558
- }
559
-
560
- getChildNodes(): FormNode[] {
561
- if (this.overrideChildren) return this.overrideChildren;
562
- let children = this.definition.children;
563
- if (this.definition.childRefId) {
564
- const ref = this.tree.controlMap[this.definition.childRefId];
565
- children = ref?.children;
566
- }
567
- return (
568
- children?.map(
569
- (x, i) => new FormNode(this.id + "/" + i, x, this.tree, this),
570
- ) ?? []
571
- );
572
- }
549
+ export interface FormNode {
550
+ id: string;
551
+ definition: ControlDefinition;
552
+ tree: FormTree;
553
+ parent?: FormNode;
554
+ getChildNodes(dontFollowRef?: boolean): FormNode[];
555
+ }
573
556
 
574
- withOverrideChildren(children: ControlDefinition[]) {
575
- return new FormNode(
576
- this.id,
577
- this.definition,
578
- this.tree,
579
- this.parent,
580
- children.map((x) => nodeForControl(x, this.tree)),
581
- );
582
- }
557
+ export function defaultGetChildNodes(
558
+ parent: FormNode,
559
+ children?: ControlDefinition[] | null,
560
+ ) {
561
+ return (
562
+ children?.map((x, i) => nodeForControl(x, parent.tree, i, parent)) ?? []
563
+ );
583
564
  }
584
565
 
585
- export interface FormTreeLookup<A = string> {
586
- getForm(formId: A): FormTree | undefined;
566
+ export interface FormTreeLookup {
567
+ getForm(formId: string): FormTree | undefined;
587
568
  }
588
569
  export interface FormTree extends FormTreeLookup {
589
570
  rootNode: FormNode;
590
- controlMap: ControlMap;
571
+ getChildrenForId(id: string, parent: FormNode): FormNode[];
591
572
  }
592
573
 
574
+ export function wrapFormNode(
575
+ node: FormNode,
576
+ getChildren?: (dontFollowRef?: boolean) => FormNode[],
577
+ ): FormNode {
578
+ return {
579
+ ...node,
580
+ getChildNodes: getChildren ?? ((dfr) => node.getChildNodes(dfr)),
581
+ };
582
+ }
593
583
  export function nodeForControl(
594
584
  definition: ControlDefinition,
595
585
  tree: FormTree,
596
586
  indexOrId?: number | string,
597
587
  parent?: FormNode,
598
588
  ): FormNode {
599
- return new FormNode(
600
- parent ? parent.id + "/" + indexOrId : "",
589
+ return {
590
+ id: parent ? parent.id + "/" + indexOrId : "",
601
591
  definition,
602
592
  tree,
603
593
  parent,
604
- );
594
+ getChildNodes(dontFollowRef?: boolean): FormNode[] {
595
+ let children = this.definition.children;
596
+ if (!dontFollowRef && this.definition.childRefId) {
597
+ return this.tree.getChildrenForId(this.definition.childRefId, this);
598
+ }
599
+ return defaultGetChildNodes(this, children);
600
+ },
601
+ };
605
602
  }
606
603
 
607
604
  export function legacyFormNode(definition: ControlDefinition) {
@@ -619,36 +616,56 @@ function getControlIds(
619
616
  : [[definition.id, definition], ...childEntries];
620
617
  }
621
618
 
622
- function makeFormTree(
623
- controls: ControlDefinition[],
624
- getForm: FormTreeLookup,
619
+ export function createFormTreeWithRoot(
620
+ createRootNode: (tree: FormTree) => FormNode,
621
+ getChildrenForId: (id: string, parent: FormNode) => FormNode[],
622
+ getForm: FormTreeLookup = { getForm: () => undefined },
625
623
  ): FormTree {
626
624
  const tree = {
627
- getForm,
628
- controlMap: Object.fromEntries(controls.flatMap(getControlIds)),
629
- } as unknown as FormTree;
630
- tree.rootNode = nodeForControl(
631
- {
632
- children: controls,
633
- type: ControlDefinitionType.Group,
625
+ ...getForm,
626
+ getChildrenForId,
627
+ rootNode: undefined! as FormNode,
628
+ } satisfies FormTree;
629
+ tree.rootNode = createRootNode(tree);
630
+ return tree;
631
+ }
632
+
633
+ export function createFormTree(
634
+ controls: ControlDefinition[],
635
+ getForm: FormTreeLookup = { getForm: () => undefined },
636
+ ): FormTree {
637
+ const controlMap = Object.fromEntries(controls.flatMap(getControlIds));
638
+ return createFormTreeWithRoot(
639
+ (tree) =>
640
+ nodeForControl(
641
+ { children: controls, type: ControlDefinitionType.Group },
642
+ tree,
643
+ ),
644
+ (id: string, parent: FormNode) => {
645
+ return (
646
+ controlMap[id]?.children?.map((x, i) =>
647
+ nodeForControl(x, parent.tree, i, parent),
648
+ ) ?? []
649
+ );
634
650
  },
635
- tree,
651
+ getForm,
636
652
  );
637
- return tree;
638
653
  }
639
654
 
640
655
  export function createFormLookup<A extends Record<string, ControlDefinition[]>>(
641
656
  formMap: A,
642
- ): FormTreeLookup<keyof A> {
657
+ ): {
658
+ getForm(formId: keyof A): FormTree;
659
+ } {
643
660
  const lookup = {
644
661
  getForm,
645
662
  };
646
663
  const forms = Object.fromEntries(
647
- Object.entries(formMap).map(([k, v]) => [k, makeFormTree(v, lookup)]),
664
+ Object.entries(formMap).map(([k, v]) => [k, createFormTree(v, lookup)]),
648
665
  );
649
666
  return lookup;
650
667
 
651
- function getForm(formId: keyof A): FormTree | undefined {
668
+ function getForm(formId: keyof A): FormTree {
652
669
  return forms[formId as string];
653
670
  }
654
671
  }
@@ -148,8 +148,8 @@ export interface FormRenderer {
148
148
  */
149
149
  renderLabel: (
150
150
  props: LabelRendererProps,
151
- labelStart: ReactNode,
152
- labelEnd: ReactNode,
151
+ labelStart?: ReactNode,
152
+ labelEnd?: ReactNode,
153
153
  ) => ReactNode;
154
154
 
155
155
  /**
@@ -172,6 +172,9 @@ export interface FormRenderer {
172
172
  * @returns A React node.
173
173
  */
174
174
  renderLabelText: (props: ReactNode) => ReactNode;
175
+ renderText: (props: ReactNode) => ReactNode;
176
+
177
+ h: (type: any, props: any, ...children: any[]) => ReactElement;
175
178
  }
176
179
 
177
180
  export interface AdornmentProps {
@@ -463,7 +466,7 @@ export function useControlRendererComponent(
463
466
  parentDataNode: SchemaDataNode,
464
467
  ): FC<{}> {
465
468
  const [definition, formNode] =
466
- controlOrFormNode instanceof FormNode
469
+ "definition" in controlOrFormNode
467
470
  ? [controlOrFormNode.definition, controlOrFormNode]
468
471
  : [controlOrFormNode, legacyFormNode(controlOrFormNode)];
469
472
  const dataProps = options.useDataHook?.(definition) ?? defaultDataProps;
@@ -1,4 +1,4 @@
1
- import { ReactNode } from "react";
1
+ import React, { ReactNode } from "react";
2
2
  import {
3
3
  ActionRendererProps,
4
4
  AdornmentProps,
@@ -11,6 +11,7 @@ import {
11
11
  GroupRendererProps,
12
12
  LabelRendererProps,
13
13
  LabelType,
14
+ VisibilityRendererProps,
14
15
  } from "./controlRender";
15
16
  import { hasOptions } from "./util";
16
17
  import {
@@ -55,10 +56,16 @@ export function createFormRenderer(
55
56
  renderArray,
56
57
  renderAdornment,
57
58
  renderLayout,
58
- renderVisibility: visibilityRenderer.render,
59
+ renderVisibility,
59
60
  renderLabelText,
61
+ renderText: defaultRenderers.renderText,
62
+ h: defaultRenderers.h,
60
63
  };
61
64
 
65
+ function renderVisibility(props: VisibilityRendererProps) {
66
+ return visibilityRenderer.render(props, formRenderers);
67
+ }
68
+
62
69
  function renderLabelText(label: ReactNode) {
63
70
  return renderLabel({ label, type: LabelType.Text }, undefined, undefined);
64
71
  }
@@ -103,28 +110,33 @@ export function createFormRenderer(
103
110
 
104
111
  const options = hasOptions(props);
105
112
  const renderType = renderOptions.type;
106
- const renderer =
107
- dataRegistrations.find(matchesRenderer) ?? defaultRenderers.data;
113
+ const renderer = dataRegistrations.find(matchesRenderer);
108
114
 
109
- const result = renderer.render(props, formRenderers);
115
+ const result = (renderer ?? defaultRenderers.data).render(
116
+ props,
117
+ formRenderers,
118
+ );
110
119
  if (typeof result === "function") return result;
111
120
  return (l) => ({ ...l, children: result });
112
121
 
113
122
  function matchesRenderer(x: DataRendererRegistration) {
123
+ const noMatch = x.match ? !x.match(props, renderOptions) : undefined;
124
+ if (noMatch === true) return false;
114
125
  const matchCollection =
115
126
  (x.collection ?? false) ===
116
127
  (props.elementIndex == null && (field.collection ?? false));
117
- const matchSchemaType =
118
- x.schemaType &&
119
- renderType == DataRenderType.Standard &&
120
- isOneOf(x.schemaType, field.type);
121
- const matchRenderType =
122
- !x.renderType || isOneOf(x.renderType, renderType);
128
+ const isSchemaAllowed =
129
+ !!x.schemaType && renderType == DataRenderType.Standard
130
+ ? isOneOf(x.schemaType, field.type)
131
+ : undefined;
132
+ const isRendererAllowed =
133
+ !!x.renderType && isOneOf(x.renderType, renderType);
123
134
  return (
124
135
  matchCollection &&
125
136
  (x.options ?? false) === options &&
126
- (matchSchemaType || matchRenderType) &&
127
- (!x.match || x.match(props, renderOptions))
137
+ (isSchemaAllowed ||
138
+ isRendererAllowed ||
139
+ (!x.renderType && !x.schemaType && noMatch === false))
128
140
  );
129
141
  }
130
142
  }
package/src/renderers.tsx CHANGED
@@ -19,7 +19,8 @@ import {
19
19
  ControlAdornment,
20
20
  ControlAdornmentType,
21
21
  IconAdornment,
22
- OptionalAdornment, RenderOptions,
22
+ OptionalAdornment,
23
+ RenderOptions,
23
24
  SetFieldAdornment,
24
25
  } from "./controlDefinition";
25
26
 
@@ -33,6 +34,8 @@ export interface DefaultRenderers {
33
34
  adornment: AdornmentRendererRegistration;
34
35
  renderLayout: LayoutRendererRegistration;
35
36
  visibility: VisibilityRendererRegistration;
37
+ renderText: (props: ReactNode) => ReactNode;
38
+ h: (type: any, props: any, ...children: any[]) => ReactElement;
36
39
  }
37
40
 
38
41
  export interface LayoutRendererRegistration {
@@ -104,7 +107,7 @@ export interface AdornmentRendererRegistration {
104
107
 
105
108
  export interface VisibilityRendererRegistration {
106
109
  type: "visibility";
107
- render: (props: VisibilityRendererProps) => ReactNode;
110
+ render: (props: VisibilityRendererProps, renderer: FormRenderer) => ReactNode;
108
111
  }
109
112
 
110
113
  export type RendererRegistration =
package/tsconfig.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "moduleResolution": "node",
14
14
  "resolveJsonModule": true,
15
15
  "isolatedModules": true,
16
- "jsx": "react",
16
+ "jsx": "preserve",
17
17
  "outDir": "lib"
18
18
  },
19
19
  "include": ["src/**/*.ts", "src/**/*.tsx"],