@react-typed-forms/schemas 14.2.0 → 14.3.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.
package/src/hooks.tsx ADDED
@@ -0,0 +1,459 @@
1
+ import {
2
+ ControlDefinition,
3
+ DynamicPropertyType,
4
+ ControlDataContext,
5
+ getRootDataNode,
6
+ getJsonPath,
7
+ isDataControl,
8
+ } from "./controlDefinition";
9
+ import React, { useEffect, useMemo, useRef } from "react";
10
+ import {
11
+ addAfterChangesCallback,
12
+ collectChanges,
13
+ Control,
14
+ SubscriptionTracker,
15
+ trackedValue,
16
+ useCalculatedControl,
17
+ useComputed,
18
+ useControl,
19
+ useRefState,
20
+ } from "@react-typed-forms/core";
21
+
22
+ import {
23
+ defaultValueForField,
24
+ elementValueForField,
25
+ getDisplayOnlyOptions,
26
+ isControlDisabled,
27
+ isControlReadonly,
28
+ JsonPath,
29
+ jsonPathString,
30
+ } from "./util";
31
+ import jsonata from "jsonata";
32
+ import { v4 as uuidv4 } from "uuid";
33
+ import { DynamicHookGenerator, HookDep, toDepString } from "./dynamicHooks";
34
+ import {
35
+ schemaDataForFieldRef,
36
+ SchemaDataNode,
37
+ SchemaInterface,
38
+ } from "./schemaField";
39
+ import {
40
+ DataExpression,
41
+ DataMatchExpression,
42
+ EntityExpression,
43
+ ExpressionType,
44
+ JsonataExpression,
45
+ NotEmptyExpression,
46
+ } from "./entityExpression";
47
+
48
+ export type EvalExpressionHook<A = any> = DynamicHookGenerator<
49
+ Control<A | undefined>,
50
+ ControlDataContext
51
+ >;
52
+
53
+ export type UseEvalExpressionHook = (
54
+ expr: EntityExpression | undefined | null,
55
+ coerce: (v: any) => any,
56
+ ) => DynamicHookGenerator<Control<any> | undefined, ControlDataContext>;
57
+
58
+ export function useEvalVisibilityHook(
59
+ useEvalExpressionHook: UseEvalExpressionHook,
60
+ definition: ControlDefinition,
61
+ overrideDataNode?: SchemaDataNode,
62
+ ): EvalExpressionHook<boolean> {
63
+ const dynamicVisibility = useEvalDynamicBoolHook(
64
+ definition,
65
+ DynamicPropertyType.Visible,
66
+ useEvalExpressionHook,
67
+ );
68
+ return makeDynamicPropertyHook(
69
+ dynamicVisibility,
70
+ (ctx, { definition, overrideDataNode }) =>
71
+ useComputed(() => {
72
+ const dataNode = overrideDataNode ?? ctx.dataNode;
73
+ return (
74
+ !dataNode ||
75
+ (matchesType(dataNode) &&
76
+ !hideDisplayOnly(dataNode, ctx.schemaInterface, definition))
77
+ );
78
+ }),
79
+ { definition, overrideDataNode },
80
+ );
81
+ }
82
+
83
+ export function useEvalReadonlyHook(
84
+ useEvalExpressionHook: UseEvalExpressionHook,
85
+ definition: ControlDefinition,
86
+ ): EvalExpressionHook<boolean> {
87
+ const dynamicReadonly = useEvalDynamicBoolHook(
88
+ definition,
89
+ DynamicPropertyType.Readonly,
90
+ useEvalExpressionHook,
91
+ );
92
+ return makeDynamicPropertyHook(
93
+ dynamicReadonly,
94
+ (ctx, { definition }) =>
95
+ useCalculatedControl(() => isControlReadonly(definition)),
96
+ { definition },
97
+ );
98
+ }
99
+
100
+ export function useEvalStyleHook(
101
+ useEvalExpressionHook: UseEvalExpressionHook,
102
+ property: DynamicPropertyType,
103
+ definition: ControlDefinition,
104
+ ): EvalExpressionHook<React.CSSProperties> {
105
+ const dynamicStyle = useEvalDynamicHook(
106
+ definition,
107
+ property,
108
+ useEvalExpressionHook,
109
+ );
110
+ return makeDynamicPropertyHook(
111
+ dynamicStyle,
112
+ () => useControl(undefined),
113
+ undefined,
114
+ );
115
+ }
116
+
117
+ export function useEvalAllowedOptionsHook(
118
+ useEvalExpressionHook: UseEvalExpressionHook,
119
+ definition: ControlDefinition,
120
+ ): EvalExpressionHook<any[]> {
121
+ const dynamicAllowed = useEvalDynamicHook(
122
+ definition,
123
+ DynamicPropertyType.AllowedOptions,
124
+ useEvalExpressionHook,
125
+ );
126
+ return makeDynamicPropertyHook(
127
+ dynamicAllowed,
128
+ () => useControl([]),
129
+ undefined,
130
+ );
131
+ }
132
+
133
+ export function useEvalDisabledHook(
134
+ useEvalExpressionHook: UseEvalExpressionHook,
135
+ definition: ControlDefinition,
136
+ ): EvalExpressionHook<boolean> {
137
+ const dynamicDisabled = useEvalDynamicBoolHook(
138
+ definition,
139
+ DynamicPropertyType.Disabled,
140
+ useEvalExpressionHook,
141
+ );
142
+ return makeDynamicPropertyHook(
143
+ dynamicDisabled,
144
+ (ctx) =>
145
+ useComputed(() => {
146
+ const dataControl = ctx.dataNode?.control;
147
+ const setToNull = dataControl?.meta["nullControl"]?.value === false;
148
+ return setToNull || isControlDisabled(definition);
149
+ }),
150
+ undefined,
151
+ );
152
+ }
153
+
154
+ export function useEvalDisplayHook(
155
+ useEvalExpressionHook: UseEvalExpressionHook,
156
+ definition: ControlDefinition,
157
+ ): DynamicHookGenerator<
158
+ Control<string | undefined> | undefined,
159
+ ControlDataContext
160
+ > {
161
+ return useEvalDynamicHook<string | undefined>(
162
+ definition,
163
+ DynamicPropertyType.Display,
164
+ useEvalExpressionHook,
165
+ );
166
+ }
167
+ export function useEvalDefaultValueHook(
168
+ useEvalExpressionHook: UseEvalExpressionHook,
169
+ definition: ControlDefinition,
170
+ ): EvalExpressionHook {
171
+ const dynamicValue = useEvalDynamicHook(
172
+ definition,
173
+ DynamicPropertyType.DefaultValue,
174
+ useEvalExpressionHook,
175
+ );
176
+ return makeDynamicPropertyHook(
177
+ dynamicValue,
178
+ (ctx, { definition }) => {
179
+ return useComputed(calcDefault);
180
+ function calcDefault() {
181
+ const [required, dcv] = isDataControl(definition)
182
+ ? [definition.required, definition.defaultValue]
183
+ : [false, undefined];
184
+ const field = ctx.dataNode?.schema.field;
185
+ return (
186
+ dcv ??
187
+ (field
188
+ ? ctx.dataNode!.elementIndex != null
189
+ ? elementValueForField(field)
190
+ : defaultValueForField(field, required)
191
+ : undefined)
192
+ );
193
+ }
194
+ },
195
+ { definition },
196
+ );
197
+ }
198
+
199
+ function useDataExpression(
200
+ fvExpr: DataExpression,
201
+ node: SchemaDataNode,
202
+ coerce: (v: any) => any = (x) => x,
203
+ ) {
204
+ const otherField = schemaDataForFieldRef(fvExpr.field, node);
205
+ return useCalculatedControl(() => coerce(otherField.control?.value));
206
+ }
207
+
208
+ function useDataMatchExpression(
209
+ fvExpr: DataMatchExpression,
210
+ node: SchemaDataNode,
211
+ coerce: (v: any) => any = (x) => x,
212
+ ) {
213
+ const otherField = schemaDataForFieldRef(fvExpr.field, node);
214
+ return useCalculatedControl(() => {
215
+ const fv = otherField.control?.value;
216
+ return coerce(
217
+ Array.isArray(fv) ? fv.includes(fvExpr.value) : fv === fvExpr.value,
218
+ );
219
+ });
220
+ }
221
+
222
+ function useNotEmptyExpression(
223
+ fvExpr: NotEmptyExpression,
224
+ node: SchemaDataNode,
225
+ schemaInterface: SchemaInterface,
226
+ coerce: (v: any) => any = (x) => x,
227
+ ) {
228
+ const otherField = schemaDataForFieldRef(fvExpr.field, node);
229
+ return useCalculatedControl(() => {
230
+ const fv = otherField.control?.value;
231
+ const field = otherField.schema.field;
232
+ return coerce(field && !schemaInterface.isEmptyValue(field, fv));
233
+ });
234
+ }
235
+
236
+ export function defaultEvalHooks(
237
+ expr: EntityExpression,
238
+ context: ControlDataContext,
239
+ coerce: (v: any) => any,
240
+ ) {
241
+ switch (expr.type) {
242
+ case ExpressionType.Jsonata:
243
+ const bindings = useComputed(() => ({ formData: context.formData }));
244
+ return useJsonataExpression(
245
+ (expr as JsonataExpression).expression,
246
+ getRootDataNode(context.parentNode).control!,
247
+ getJsonPath(context.parentNode),
248
+ bindings,
249
+ coerce,
250
+ );
251
+ case ExpressionType.UUID:
252
+ return useUuidExpression(coerce);
253
+ case ExpressionType.Data:
254
+ return useDataExpression(
255
+ expr as DataExpression,
256
+ context.parentNode,
257
+ coerce,
258
+ );
259
+ case ExpressionType.DataMatch:
260
+ return useDataMatchExpression(
261
+ expr as DataMatchExpression,
262
+ context.parentNode,
263
+ coerce,
264
+ );
265
+ case ExpressionType.NotEmpty:
266
+ return useNotEmptyExpression(
267
+ expr as NotEmptyExpression,
268
+ context.parentNode,
269
+ context.schemaInterface,
270
+ coerce,
271
+ );
272
+ default:
273
+ return useControl(undefined);
274
+ }
275
+ }
276
+
277
+ export const defaultUseEvalExpressionHook =
278
+ makeEvalExpressionHook(defaultEvalHooks);
279
+
280
+ export function makeEvalExpressionHook(
281
+ f: (
282
+ expr: EntityExpression,
283
+ context: ControlDataContext,
284
+ coerce: (v: any) => any,
285
+ ) => Control<any>,
286
+ ): UseEvalExpressionHook {
287
+ return (expr, coerce) => ({
288
+ deps: expr?.type,
289
+ state: expr && expr.type ? expr : undefined,
290
+ runHook: (ctx: ControlDataContext, state: EntityExpression | undefined) => {
291
+ return state ? f(state, ctx, coerce) : undefined;
292
+ },
293
+ });
294
+ }
295
+
296
+ export function useEvalDynamicBoolHook(
297
+ definition: ControlDefinition,
298
+ type: DynamicPropertyType,
299
+ useEvalExpressionHook: UseEvalExpressionHook,
300
+ ): DynamicHookGenerator<Control<any> | undefined, ControlDataContext> {
301
+ return useEvalDynamicHook(definition, type, useEvalExpressionHook, (x) =>
302
+ Boolean(x),
303
+ );
304
+ }
305
+
306
+ export function useEvalDynamicHook<V>(
307
+ definition: ControlDefinition,
308
+ type: DynamicPropertyType,
309
+ useEvalExpressionHook: UseEvalExpressionHook,
310
+ coerce: (v: any) => any = (x) => x,
311
+ ): DynamicHookGenerator<Control<V> | undefined, ControlDataContext> {
312
+ const expression = definition.dynamic?.find((x) => x.type === type);
313
+ return useEvalExpressionHook(
314
+ expression?.expr,
315
+ coerce,
316
+ ) as DynamicHookGenerator<Control<V> | undefined, ControlDataContext>;
317
+ }
318
+
319
+ export function matchesType(context: SchemaDataNode): boolean {
320
+ const types = context.schema.field.onlyForTypes;
321
+ if (types == null || types.length === 0) return true;
322
+ const parent = context.parent!;
323
+ const typeNode = parent.schema
324
+ .getChildNodes()
325
+ .find((x) => x.field.isTypeField);
326
+ if (typeNode == null) return true;
327
+ const typeField = parent.getChild(typeNode).control as Control<string>;
328
+ return typeField && types.includes(typeField.value);
329
+ }
330
+
331
+ export function hideDisplayOnly(
332
+ context: SchemaDataNode,
333
+ schemaInterface: SchemaInterface,
334
+ definition: ControlDefinition,
335
+ ) {
336
+ const displayOptions = getDisplayOnlyOptions(definition);
337
+ return (
338
+ displayOptions &&
339
+ !displayOptions.emptyText &&
340
+ schemaInterface.isEmptyValue(context.schema.field, context.control?.value)
341
+ );
342
+ }
343
+
344
+ export function useUuidExpression(coerce: (v: any) => any = (x) => x) {
345
+ return useControl(() => coerce(uuidv4()));
346
+ }
347
+
348
+ export function useJsonataExpression(
349
+ jExpr: string,
350
+ data: Control<any>,
351
+ path: JsonPath[],
352
+ bindings?: Control<Record<string, any>>,
353
+ coerce: (v: any) => any = (x) => x,
354
+ ): Control<any> {
355
+ const pathString = jsonPathString(path, (x) => `#$i[${x}]`);
356
+ const fullExpr = pathString ? pathString + ".(" + jExpr + ")" : jExpr;
357
+ const compiledExpr = useMemo(() => {
358
+ try {
359
+ return jsonata(jExpr ? fullExpr : "null");
360
+ } catch (e) {
361
+ console.error(e);
362
+ return jsonata("null");
363
+ }
364
+ }, [fullExpr]);
365
+ const control = useControl();
366
+ const listenerRef = useRef<() => void>();
367
+ const updateRef = useRef(0);
368
+ const [ref] = useRefState(
369
+ () =>
370
+ new SubscriptionTracker(() => {
371
+ const l = listenerRef.current;
372
+ if (l) {
373
+ listenerRef.current = undefined;
374
+ addAfterChangesCallback(() => {
375
+ listenerRef.current = l;
376
+ l();
377
+ });
378
+ }
379
+ }),
380
+ );
381
+ useEffect(() => {
382
+ listenerRef.current = apply;
383
+ apply();
384
+ async function apply() {
385
+ const tracker = ref.current;
386
+ try {
387
+ updateRef.current++;
388
+ control.value = coerce(
389
+ await compiledExpr.evaluate(
390
+ trackedValue(data, tracker.collectUsage),
391
+ collectChanges(tracker.collectUsage, () => bindings?.value),
392
+ ),
393
+ );
394
+ } finally {
395
+ if (!--updateRef.current) tracker.update();
396
+ }
397
+ }
398
+ }, [compiledExpr]);
399
+ useEffect(() => {
400
+ return () => {
401
+ listenerRef.current = undefined;
402
+ ref.current.cleanup();
403
+ };
404
+ }, []);
405
+ return control;
406
+ }
407
+
408
+ export function useEvalActionHook(
409
+ useExpr: UseEvalExpressionHook,
410
+ definition: ControlDefinition,
411
+ ): EvalExpressionHook<string | null> {
412
+ const dynamicValue = useEvalDynamicHook(
413
+ definition,
414
+ DynamicPropertyType.ActionData,
415
+ useExpr,
416
+ );
417
+ return makeDynamicPropertyHook(
418
+ dynamicValue,
419
+ () => useControl(null),
420
+ undefined,
421
+ );
422
+ }
423
+
424
+ export function useEvalLabelText(
425
+ useExpr: UseEvalExpressionHook,
426
+ definition: ControlDefinition,
427
+ ): EvalExpressionHook<string | null> {
428
+ const dynamicValue = useEvalDynamicHook(
429
+ definition,
430
+ DynamicPropertyType.Label,
431
+ useExpr,
432
+ );
433
+ return makeDynamicPropertyHook(
434
+ dynamicValue,
435
+ () => useControl(null),
436
+ undefined,
437
+ );
438
+ }
439
+
440
+ function makeDynamicPropertyHook<A, S = undefined>(
441
+ dynamicValue: DynamicHookGenerator<
442
+ Control<any> | undefined,
443
+ ControlDataContext
444
+ >,
445
+ makeDefault: (ctx: ControlDataContext, s: S) => Control<A | undefined>,
446
+ state: S,
447
+ deps?: HookDep,
448
+ ): EvalExpressionHook<A> {
449
+ return {
450
+ deps:
451
+ deps === undefined
452
+ ? dynamicValue.deps
453
+ : [deps, dynamicValue.deps].map(toDepString).join(),
454
+ runHook: (ctx, s) => {
455
+ return dynamicValue.runHook(ctx, s[0])?.as() ?? makeDefault(ctx, s[1]);
456
+ },
457
+ state: [dynamicValue.state, state],
458
+ };
459
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./controlDefinition";
2
+ export * from "./schemaBuilder";
3
+ export * from "./controlBuilder";
4
+ export * from "./controlRender";
5
+ export * from "./util";
6
+ export * from "./renderers";
7
+ export * from "./validators";
8
+ export * from "./hooks";
9
+ export * from "./defaultSchemaInterface";
10
+ export * from "./createFormRenderer";
11
+ export * from "./dynamicHooks";
12
+ export * from "./schemaValidator";
13
+ export * from "./schemaField";
14
+ export * from "./entityExpression";
@@ -0,0 +1,205 @@
1
+ import { ReactElement, ReactNode } from "react";
2
+ import {
3
+ ActionRendererProps,
4
+ AdornmentProps,
5
+ AdornmentRenderer,
6
+ ArrayRendererProps,
7
+ ControlLayoutProps,
8
+ DataRendererProps,
9
+ DisplayRendererProps,
10
+ FormRenderer,
11
+ GroupRendererProps,
12
+ LabelRendererProps,
13
+ LabelType,
14
+ RenderedControl,
15
+ VisibilityRendererProps,
16
+ } from "./controlRender";
17
+ import {
18
+ AccordionAdornment,
19
+ ControlAdornment,
20
+ ControlAdornmentType,
21
+ IconAdornment,
22
+ OptionalAdornment, RenderOptions,
23
+ SetFieldAdornment,
24
+ } from "./controlDefinition";
25
+
26
+ export interface DefaultRenderers {
27
+ data: DataRendererRegistration;
28
+ label: LabelRendererRegistration;
29
+ action: ActionRendererRegistration;
30
+ array: ArrayRendererRegistration;
31
+ group: GroupRendererRegistration;
32
+ display: DisplayRendererRegistration;
33
+ adornment: AdornmentRendererRegistration;
34
+ renderLayout: LayoutRendererRegistration;
35
+ visibility: VisibilityRendererRegistration;
36
+ }
37
+
38
+ export interface LayoutRendererRegistration {
39
+ type: "layout";
40
+ match?: (props: ControlLayoutProps) => boolean;
41
+ render: (
42
+ props: ControlLayoutProps,
43
+ renderers: FormRenderer,
44
+ ) => RenderedControl;
45
+ }
46
+ export interface DataRendererRegistration {
47
+ type: "data";
48
+ schemaType?: string | string[];
49
+ renderType?: string | string[];
50
+ options?: boolean;
51
+ collection?: boolean;
52
+ match?: (props: DataRendererProps, renderOptions: RenderOptions) => boolean;
53
+ render: (
54
+ props: DataRendererProps,
55
+ renderers: FormRenderer,
56
+ ) => ReactNode | ((layout: ControlLayoutProps) => ControlLayoutProps);
57
+ }
58
+
59
+ export interface LabelRendererRegistration {
60
+ type: "label";
61
+ labelType?: LabelType | LabelType[];
62
+ render: (
63
+ labelProps: LabelRendererProps,
64
+ labelStart: ReactNode,
65
+ labelEnd: ReactNode,
66
+ renderers: FormRenderer,
67
+ ) => ReactNode;
68
+ }
69
+
70
+ export interface ActionRendererRegistration {
71
+ type: "action";
72
+ actionType?: string | string[];
73
+ render: (props: ActionRendererProps, renderers: FormRenderer) => ReactElement;
74
+ }
75
+
76
+ export interface ArrayRendererRegistration {
77
+ type: "array";
78
+ render: (props: ArrayRendererProps, renderers: FormRenderer) => ReactElement;
79
+ }
80
+
81
+ export interface GroupRendererRegistration {
82
+ type: "group";
83
+ renderType?: string | string[];
84
+ render: (
85
+ props: GroupRendererProps,
86
+ renderers: FormRenderer,
87
+ ) => ReactElement | ((layout: ControlLayoutProps) => ControlLayoutProps);
88
+ }
89
+
90
+ export interface DisplayRendererRegistration {
91
+ type: "display";
92
+ renderType?: string | string[];
93
+ render: (
94
+ props: DisplayRendererProps,
95
+ renderers: FormRenderer,
96
+ ) => ReactElement;
97
+ }
98
+
99
+ export interface AdornmentRendererRegistration {
100
+ type: "adornment";
101
+ adornmentType?: string | string[];
102
+ render: (props: AdornmentProps, renderers: FormRenderer) => AdornmentRenderer;
103
+ }
104
+
105
+ export interface VisibilityRendererRegistration {
106
+ type: "visibility";
107
+ render: (props: VisibilityRendererProps) => ReactNode;
108
+ }
109
+
110
+ export type RendererRegistration =
111
+ | DataRendererRegistration
112
+ | GroupRendererRegistration
113
+ | DisplayRendererRegistration
114
+ | ActionRendererRegistration
115
+ | LabelRendererRegistration
116
+ | ArrayRendererRegistration
117
+ | AdornmentRendererRegistration
118
+ | LayoutRendererRegistration
119
+ | VisibilityRendererRegistration;
120
+
121
+ export function isIconAdornment(a: ControlAdornment): a is IconAdornment {
122
+ return a.type === ControlAdornmentType.Icon;
123
+ }
124
+
125
+ export function isAccordionAdornment(
126
+ a: ControlAdornment,
127
+ ): a is AccordionAdornment {
128
+ return a.type === ControlAdornmentType.Accordion;
129
+ }
130
+
131
+ export function isSetFieldAdornment(
132
+ a: ControlAdornment,
133
+ ): a is SetFieldAdornment {
134
+ return a.type === ControlAdornmentType.SetField;
135
+ }
136
+
137
+ export function isOptionalAdornment(
138
+ a: ControlAdornment,
139
+ ): a is OptionalAdornment {
140
+ return a.type === ControlAdornmentType.Optional;
141
+ }
142
+
143
+ export function createLayoutRenderer(
144
+ render: LayoutRendererRegistration["render"],
145
+ options?: Partial<LayoutRendererRegistration>,
146
+ ): LayoutRendererRegistration {
147
+ return { type: "layout", render, ...options };
148
+ }
149
+
150
+ export function createActionRenderer(
151
+ actionId: string | string[] | undefined,
152
+ render: ActionRendererRegistration["render"],
153
+ options?: Partial<ActionRendererRegistration>,
154
+ ): ActionRendererRegistration {
155
+ return { type: "action", actionType: actionId, render, ...options };
156
+ }
157
+
158
+ export function createArrayRenderer(
159
+ render: ArrayRendererRegistration["render"],
160
+ options?: Partial<ArrayRendererRegistration>,
161
+ ): ArrayRendererRegistration {
162
+ return { type: "array", render, ...options };
163
+ }
164
+
165
+ export function createDataRenderer(
166
+ render: DataRendererRegistration["render"],
167
+ options?: Partial<DataRendererRegistration>,
168
+ ): DataRendererRegistration {
169
+ return { type: "data", render, ...options };
170
+ }
171
+
172
+ export function createGroupRenderer(
173
+ render: GroupRendererRegistration["render"],
174
+ options?: Partial<GroupRendererRegistration>,
175
+ ): GroupRendererRegistration {
176
+ return { type: "group", render, ...options };
177
+ }
178
+
179
+ export function createDisplayRenderer(
180
+ render: DisplayRendererRegistration["render"],
181
+ options?: Partial<DisplayRendererRegistration>,
182
+ ): DisplayRendererRegistration {
183
+ return { type: "display", render, ...options };
184
+ }
185
+
186
+ export function createLabelRenderer(
187
+ render: LabelRendererRegistration["render"],
188
+ options?: Partial<LabelRendererRegistration>,
189
+ ): LabelRendererRegistration {
190
+ return { type: "label", render, ...options };
191
+ }
192
+
193
+ export function createVisibilityRenderer(
194
+ render: VisibilityRendererRegistration["render"],
195
+ options?: Partial<VisibilityRendererRegistration>,
196
+ ): VisibilityRendererRegistration {
197
+ return { type: "visibility", render, ...options };
198
+ }
199
+
200
+ export function createAdornmentRenderer(
201
+ render: AdornmentRendererRegistration["render"],
202
+ options?: Partial<AdornmentRendererRegistration>,
203
+ ): AdornmentRendererRegistration {
204
+ return { type: "adornment", ...options, render };
205
+ }