@react-typed-forms/schemas 8.0.0 → 8.2.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 {};
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
@@ -37,6 +37,8 @@ export declare function lookupChildControl(data: DataContext, child: JsonPath):
37
37
  export declare function cleanDataForSchema(v: {
38
38
  [k: string]: any;
39
39
  } | undefined, fields: SchemaField[]): any;
40
- export declare function getAllReferencedClasses(c: ControlDefinition): string[];
41
- export declare function jsonPathString(jsonPath: JsonPath[]): string;
40
+ export declare function getAllReferencedClasses(c: ControlDefinition, collectExtra?: (c: ControlDefinition) => (string | undefined | null)[]): string[];
41
+ export declare function jsonPathString(jsonPath: JsonPath[], customIndex?: (n: number) => string): string;
42
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": "8.0.0",
3
+ "version": "8.2.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -53,6 +53,7 @@ import {
53
53
  useEvalDisabledHook,
54
54
  useEvalDisplayHook,
55
55
  UseEvalExpressionHook,
56
+ useEvalLabelText,
56
57
  useEvalReadonlyHook,
57
58
  useEvalStyleHook,
58
59
  useEvalVisibilityHook,
@@ -101,7 +102,7 @@ export interface ArrayRendererProps {
101
102
  elementCount: number;
102
103
  renderElement: (elemIndex: number) => ReactNode;
103
104
  elementKey: (elemIndex: number) => Key;
104
- arrayControl?: Control<any[] | undefined | null>;
105
+ arrayControl: Control<any[] | undefined | null>;
105
106
  className?: string;
106
107
  style?: React.CSSProperties;
107
108
  }
@@ -153,6 +154,7 @@ export interface LabelRendererProps {
153
154
  label: ReactNode;
154
155
  required?: boolean | null;
155
156
  forId?: string;
157
+ className?: string;
156
158
  }
157
159
  export interface DisplayRendererProps {
158
160
  data: DisplayData;
@@ -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
@@ -377,6 +381,7 @@ export function useControlRenderer(
377
381
  formOptions: myOptions,
378
382
  dataContext: controlDataContext,
379
383
  control: displayControl ?? control,
384
+ labelText,
380
385
  schemaField,
381
386
  displayControl,
382
387
  style: customStyle,
@@ -403,6 +408,7 @@ export function useControlRenderer(
403
408
  useCustomStyle,
404
409
  useLayoutStyle,
405
410
  useAllowedOptions,
411
+ useLabelText,
406
412
  useDynamicDisplay,
407
413
  useValidation,
408
414
  renderer,
@@ -574,6 +580,7 @@ export interface RenderControlProps {
574
580
  formOptions: FormContextOptions;
575
581
  dataContext: ControlDataContext;
576
582
  control?: Control<any>;
583
+ labelText?: Control<string | null | undefined>;
577
584
  schemaField?: SchemaField;
578
585
  displayControl?: Control<string | undefined>;
579
586
  style?: React.CSSProperties;
@@ -590,6 +597,7 @@ export function renderControlLayout({
590
597
  createDataProps: dataProps,
591
598
  displayControl,
592
599
  style,
600
+ labelText,
593
601
  allowedOptions,
594
602
  }: RenderControlProps): ControlLayoutProps {
595
603
  if (isDataControlDefinition(c)) {
@@ -609,7 +617,8 @@ export function renderControlLayout({
609
617
  groupProps(c, childRenderer, dataContext, c.styleClass, style),
610
618
  ),
611
619
  label: {
612
- label: c.title,
620
+ label: labelText?.value ?? c.title,
621
+ className: cc(c.labelClass),
613
622
  type: LabelType.Group,
614
623
  hide: c.groupOptions?.hideTitle,
615
624
  },
@@ -618,7 +627,7 @@ export function renderControlLayout({
618
627
  if (isActionControlsDefinition(c)) {
619
628
  return {
620
629
  children: renderer.renderAction({
621
- actionText: c.title ?? c.actionId,
630
+ actionText: labelText?.value ?? c.title ?? c.actionId,
622
631
  actionId: c.actionId,
623
632
  onClick: () => {},
624
633
  className: cc(c.styleClass),
@@ -664,17 +673,18 @@ export function renderControlLayout({
664
673
  : undefined,
665
674
  });
666
675
 
667
- const labelText = !c.hideTitle
668
- ? controlTitle(c.title, schemaField)
676
+ const label = !c.hideTitle
677
+ ? controlTitle(labelText?.value ?? c.title, schemaField)
669
678
  : undefined;
670
679
  return {
671
680
  processLayout: renderer.renderData(props),
672
681
  label: {
673
682
  type: LabelType.Control,
674
- label: labelText,
683
+ label,
675
684
  forId: props.id,
676
685
  required: c.required,
677
686
  hide: c.hideTitle,
687
+ className: cc(c.labelClass),
678
688
  },
679
689
  errorControl: childControl,
680
690
  };
package/src/hooks.tsx CHANGED
@@ -306,7 +306,7 @@ 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
310
  const fullExpr = pathString ? pathString + ".(" + jExpr + ")" : jExpr;
311
311
  const compiledExpr = useMemo(() => {
312
312
  try {
@@ -351,3 +351,23 @@ export function useJsonataExpression(
351
351
  }, [compiledExpr]);
352
352
  return control;
353
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
@@ -48,7 +48,7 @@ import {
48
48
  SchemaInterface,
49
49
  TextDisplay,
50
50
  } from "./types";
51
- import { hasOptions } from "./util";
51
+ import { getOverrideClass, hasOptions, rendererClass } from "./util";
52
52
 
53
53
  export interface DefaultRenderers {
54
54
  data: DataRendererRegistration;
@@ -265,6 +265,7 @@ export function createFormRenderer(
265
265
  interface DefaultLabelRendererOptions {
266
266
  className?: string;
267
267
  groupLabelClass?: string;
268
+ controlLabelClass?: string;
268
269
  requiredElement?: ReactNode;
269
270
  }
270
271
 
@@ -287,16 +288,21 @@ export function createDefaultActionRenderer(
287
288
  export function createDefaultLabelRenderer(
288
289
  options: DefaultLabelRendererOptions = { requiredElement: <span> *</span> },
289
290
  ): LabelRendererRegistration {
290
- const { className, groupLabelClass, requiredElement } = options;
291
+ const { className, groupLabelClass, controlLabelClass, requiredElement } =
292
+ options;
291
293
  return {
292
294
  render: (props, labelStart, labelEnd) => (
293
295
  <>
294
296
  {labelStart}
295
297
  <label
296
298
  htmlFor={props.forId}
297
- className={clsx(
298
- className,
299
- 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
+ ),
300
306
  )}
301
307
  >
302
308
  {props.label}
@@ -436,7 +442,10 @@ export function createDefaultGroupRenderer(
436
442
  return {
437
443
  ...cp,
438
444
  children: (
439
- <div className={clsx(props.className, className, gcn)} style={style}>
445
+ <div
446
+ className={rendererClass(props.className, clsx(className, gcn))}
447
+ style={style}
448
+ >
440
449
  {children?.map((c, i) => renderChild(i, i))}
441
450
  </div>
442
451
  ),
@@ -472,14 +481,17 @@ export function DefaultDisplay({
472
481
  <i
473
482
  style={style}
474
483
  className={clsx(
475
- className,
484
+ getOverrideClass(className),
476
485
  display ? display.value : (data as IconDisplay).iconClass,
477
486
  )}
478
487
  />
479
488
  );
480
489
  case DisplayDataType.Text:
481
490
  return (
482
- <div style={style} className={clsx(className, options.textClassName)}>
491
+ <div
492
+ style={style}
493
+ className={rendererClass(className, options.textClassName)}
494
+ >
483
495
  {display ? display.value : (data as TextDisplay).text}
484
496
  </div>
485
497
  );
@@ -487,7 +499,7 @@ export function DefaultDisplay({
487
499
  return (
488
500
  <div
489
501
  style={style}
490
- className={clsx(className, options.htmlClassName)}
502
+ className={rendererClass(className, options.htmlClassName)}
491
503
  dangerouslySetInnerHTML={{
492
504
  __html: display ? display.value ?? "" : (data as HtmlDisplay).html,
493
505
  }}
@@ -573,7 +585,7 @@ export function createDefaultDataRenderer(
573
585
  />
574
586
  ) : (
575
587
  <ControlInput
576
- className={clsx(props.className, inputClass)}
588
+ className={rendererClass(props.className, inputClass)}
577
589
  style={props.style}
578
590
  id={props.id}
579
591
  readOnly={props.readonly}
@@ -605,7 +617,7 @@ export function DefaultDisplayOnly({
605
617
  ? emptyText
606
618
  : schemaInterface.textValue(field, v)) ?? "";
607
619
  return (
608
- <div style={style} className={className}>
620
+ <div style={style} className={rendererClass(className)}>
609
621
  {text}
610
622
  </div>
611
623
  );
@@ -693,7 +705,10 @@ function createDefaultLayoutRenderer(
693
705
  ) {
694
706
  return createLayoutRenderer((props, renderers) => {
695
707
  const layout = renderLayoutParts(
696
- { ...props, className: clsx(props.className, options.className) },
708
+ {
709
+ ...props,
710
+ className: rendererClass(props.className, options.className),
711
+ },
697
712
  renderers,
698
713
  );
699
714
  return {
@@ -832,7 +847,7 @@ export function createSelectRenderer(options: SelectRendererOptions = {}) {
832
847
  return createDataRenderer(
833
848
  (props, asArray) => (
834
849
  <SelectDataRenderer
835
- className={clsx(props.className, options.className)}
850
+ className={rendererClass(props.className, options.className)}
836
851
  state={props.control}
837
852
  id={props.id}
838
853
  options={props.options!}
@@ -963,7 +978,6 @@ export function DefaultVisibility({
963
978
 
964
979
  export function DefaultLayout({
965
980
  errorClass,
966
- className,
967
981
  layout: { controlEnd, controlStart, label, children, errorControl },
968
982
  }: DefaultLayoutRendererOptions & {
969
983
  layout: RenderedLayout;
package/src/tailwind.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { DefaultRendererOptions } from "./renderers";
3
3
 
4
- export const defaultTailwindTheme: DefaultRendererOptions = {
4
+ export const defaultTailwindTheme = {
5
5
  label: {
6
6
  groupLabelClass: "font-bold",
7
7
  requiredElement: <span className="text-red-500"> *</span>,
@@ -26,4 +26,4 @@ export const defaultTailwindTheme: DefaultRendererOptions = {
26
26
  data: {
27
27
  displayOnlyClass: "flex flex-row items-center gap-2",
28
28
  },
29
- };
29
+ } satisfies DefaultRendererOptions;
package/src/types.ts CHANGED
@@ -65,6 +65,7 @@ export interface ControlDefinition {
65
65
  title?: string | null;
66
66
  styleClass?: string | null;
67
67
  layoutClass?: string | null;
68
+ labelClass?: string | null;
68
69
  dynamic?: DynamicProperty[] | null;
69
70
  adornments?: ControlAdornment[] | null;
70
71
  children?: ControlDefinition[] | null;
@@ -90,7 +91,8 @@ export enum DynamicPropertyType {
90
91
  Display = "Display",
91
92
  Style = "Style",
92
93
  LayoutStyle = "LayoutStyle",
93
- AllowedOptions = "AllowedOptions"
94
+ AllowedOptions = "AllowedOptions",
95
+ Label = "Label",
94
96
  }
95
97
 
96
98
  export interface EntityExpression {
package/src/util.ts CHANGED
@@ -387,20 +387,35 @@ export function cleanDataForSchema(
387
387
  return out;
388
388
  }
389
389
 
390
- export function getAllReferencedClasses(c: ControlDefinition): string[] {
391
- const childClasses = c.children?.flatMap(getAllReferencedClasses);
392
- const tc = clsx(c.styleClass, c.layoutClass);
390
+ export function getAllReferencedClasses(
391
+ c: ControlDefinition,
392
+ collectExtra?: (c: ControlDefinition) => (string | undefined | null)[],
393
+ ): string[] {
394
+ const childClasses = c.children?.flatMap((x) =>
395
+ getAllReferencedClasses(x, collectExtra),
396
+ );
397
+ const tc = clsx(
398
+ [
399
+ c.styleClass,
400
+ c.layoutClass,
401
+ c.labelClass,
402
+ ...(collectExtra?.(c) ?? []),
403
+ ].map(getOverrideClass),
404
+ );
393
405
  if (childClasses && !tc) return childClasses;
394
406
  if (!tc) return [];
395
407
  if (childClasses) return [tc, ...childClasses];
396
408
  return [tc];
397
409
  }
398
410
 
399
- export function jsonPathString(jsonPath: JsonPath[]) {
411
+ export function jsonPathString(
412
+ jsonPath: JsonPath[],
413
+ customIndex?: (n: number) => string,
414
+ ) {
400
415
  let out = "";
401
416
  jsonPath.forEach((v, i) => {
402
417
  if (typeof v === "number") {
403
- out += "[" + v + "]";
418
+ out += customIndex?.(v) ?? "[" + v + "]";
404
419
  } else {
405
420
  if (i > 0) out += ".";
406
421
  out += v;
@@ -420,3 +435,19 @@ export function findChildDefinition(
420
435
  }
421
436
  return parent.children![childPath];
422
437
  }
438
+
439
+ export function getOverrideClass(className?: string | null) {
440
+ if (className && className.startsWith("@ ")) {
441
+ return className.substring(2);
442
+ }
443
+ return className;
444
+ }
445
+
446
+ export function rendererClass(
447
+ controlClass?: string | null,
448
+ globalClass?: string | null,
449
+ ) {
450
+ const oc = getOverrideClass(controlClass);
451
+ if (oc === controlClass) return clsx(controlClass, globalClass);
452
+ return oc ? oc : undefined;
453
+ }