-
Notifications
You must be signed in to change notification settings - Fork 2
/
ValueRendererManager.tsx
201 lines (176 loc) · 6.91 KB
/
ValueRendererManager.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Properties
*/
import type * as React from "react";
import type { PropertyRecord } from "@itwin/appui-abstract";
import { PropertyValueFormat } from "@itwin/appui-abstract";
import type { Orientation } from "@itwin/core-react";
import { ArrayPropertyValueRenderer } from "./renderers/value/ArrayPropertyValueRenderer";
import { MergedPropertyValueRenderer } from "./renderers/value/MergedPropertyValueRenderer";
import { MultilineTextPropertyValueRenderer } from "./renderers/value/MultilineTextPropertyValueRenderer";
import { PrimitivePropertyValueRenderer } from "./renderers/value/PrimitivePropertyValueRenderer";
import { StructPropertyValueRenderer } from "./renderers/value/StructPropertyValueRenderer";
import { UrlPropertyValueRenderer } from "./renderers/value/UrlPropertyValueRenderer";
/** Types of property containers
* @public
*/
export enum PropertyContainerType {
PropertyPane = "pane",
Table = "table",
Tree = "tree",
}
/** State of the Dialog component in a container which renders properties
* @public
*/
export interface PropertyDialogState {
title: string;
content: React.ReactNode;
}
/** State of the Popup component in a container which renders properties
* @public
*/
export interface PropertyPopupState {
fixedPosition: { top: number; left: number };
content: React.ReactNode;
}
/** Additional parameters to the renderer
* @public
*/
export interface PropertyValueRendererContext {
/** Type of container that holds the property */
containerType?: string;
/** Style that should be applied to the rendered element */
style?: React.CSSProperties;
/** Orientation of property/container */
orientation?: Orientation;
/** Callback to request for a Popup to be shown. */
onPopupShow?: (popupState: PropertyPopupState) => void;
/** Callback to request for a Popup to be hidden. */
onPopupHide?: () => void;
/** Callback to request for Dialog to be opened. */
onDialogOpen?: (dialogState: PropertyDialogState) => void;
/** Callback to highlight text */
textHighlighter?: (text: string) => React.ReactNode;
/** Default value to show if value rendering is asynchronous */
defaultValue?: React.ReactNode;
/** Whether property value is expanded. */
isExpanded?: boolean;
/** Called when property value expansion or collapse is requested. */
onExpansionToggled?: () => void;
/** Called when property value element height changes. */
onHeightChanged?: (newHeight: number) => void;
}
/** Custom property value renderer interface
* @public
*/
export interface IPropertyValueRenderer {
/** Checks if the renderer can handle given property */
canRender: (
record: PropertyRecord,
context?: PropertyValueRendererContext
) => boolean;
/** Method that returns a JSX representation of PropertyRecord */
render: (
record: PropertyRecord,
context?: PropertyValueRendererContext
) => React.ReactNode;
}
/** Default implementation of property value renderer manager
* @public
*/
export class PropertyValueRendererManager {
private static _defaultRendererManager: PropertyValueRendererManager;
protected _propertyRenderers: Map<string, IPropertyValueRenderer> = new Map<
string,
IPropertyValueRenderer
>();
protected _defaultPrimitiveValueRenderer: IPropertyValueRenderer =
new PrimitivePropertyValueRenderer();
protected _defaultArrayValueRenderer: IPropertyValueRenderer =
new ArrayPropertyValueRenderer();
protected _defaultStructValueRenderer: IPropertyValueRenderer =
new StructPropertyValueRenderer();
protected _defaultMergedValueRenderer: IPropertyValueRenderer =
new MergedPropertyValueRenderer();
private getCustomRenderer(record: PropertyRecord) {
if (
record.property.renderer &&
this._propertyRenderers.has(record.property.renderer.name)
)
return this._propertyRenderers.get(record.property.renderer.name);
if (this._defaultMergedValueRenderer.canRender(record))
return this._defaultMergedValueRenderer;
if (this._propertyRenderers.has(record.property.typename))
return this._propertyRenderers.get(record.property.typename)!;
return undefined;
}
private selectRenderer(record: PropertyRecord) {
const customRenderer = this.getCustomRenderer(record);
if (customRenderer) return customRenderer;
// Use one of default renderers
switch (record.value.valueFormat) {
case PropertyValueFormat.Primitive:
return this._defaultPrimitiveValueRenderer;
case PropertyValueFormat.Array:
return this._defaultArrayValueRenderer;
case PropertyValueFormat.Struct:
return this._defaultStructValueRenderer;
default:
return undefined;
}
}
/** Render property into JSX element */
public render(
record: PropertyRecord,
context?: PropertyValueRendererContext
): React.ReactNode {
const selectedRenderer = this.selectRenderer(record);
if (!selectedRenderer || !selectedRenderer.canRender(record, context))
return undefined;
return selectedRenderer.render(record, context);
}
/** Register a specified property type renderer */
public registerRenderer(
rendererType: string,
propertyRenderer: IPropertyValueRenderer,
overwrite = false
) {
if (!overwrite && this._propertyRenderers.has(rendererType)) {
throw Error(
`PropertyValueRendererManager.registerRenderer error: type '${rendererType}' already registered to '${propertyRenderer.constructor.name}'`
);
}
this._propertyRenderers.set(rendererType, propertyRenderer);
}
/** Unregister a specified property type renderer */
public unregisterRenderer(rendererType: string) {
this._propertyRenderers.delete(rendererType);
}
/** Get the specified property type renderer instance */
public getRegisteredRenderer(rendererType: string) {
return this._propertyRenderers.get(rendererType);
}
/** Check whether a property record has a custom renderer registered that can render it */
public hasCustomRenderer(record: PropertyRecord): boolean {
const customRenderer = this.getCustomRenderer(record);
return !!(customRenderer && customRenderer.canRender(record));
}
/** Returns default PropertyValueRendererManager instance */
public static get defaultManager() {
if (!this._defaultRendererManager)
this._defaultRendererManager = new PropertyValueRendererManager();
return this._defaultRendererManager;
}
}
PropertyValueRendererManager.defaultManager.registerRenderer(
"url",
new UrlPropertyValueRenderer()
);
PropertyValueRendererManager.defaultManager.registerRenderer(
"multiline",
new MultilineTextPropertyValueRenderer()
);