1
- import React , { Component , } from "react"
1
+ import React , { Component } from "react"
2
2
import PropTypes from "prop-types"
3
3
import ImPropTypes from "react-immutable-proptypes"
4
4
5
5
export default class ModelWrapper extends Component {
6
-
7
6
static propTypes = {
8
7
schema : PropTypes . object . isRequired ,
9
8
name : PropTypes . string ,
@@ -13,32 +12,147 @@ export default class ModelWrapper extends Component {
13
12
getComponent : PropTypes . func . isRequired ,
14
13
getConfigs : PropTypes . func . isRequired ,
15
14
specSelectors : PropTypes . object . isRequired ,
15
+ specActions : PropTypes . object . isRequired ,
16
16
expandDepth : PropTypes . number ,
17
17
layoutActions : PropTypes . object ,
18
18
layoutSelectors : PropTypes . object . isRequired ,
19
19
includeReadOnly : PropTypes . bool ,
20
20
includeWriteOnly : PropTypes . bool ,
21
21
}
22
22
23
- onToggle = ( name , isShown ) => {
23
+ constructor ( props ) {
24
+ super ( props )
25
+ this . state = {
26
+ selectedSchema : null
27
+ }
28
+ }
29
+
30
+ onToggle = ( name , isShown ) => {
24
31
// If this prop is present, we'll have deepLinking for it
25
32
if ( this . props . layoutActions ) {
26
33
this . props . layoutActions . show ( this . props . fullPath , isShown )
27
34
}
28
35
}
29
36
30
- render ( ) {
31
- let { getComponent, getConfigs } = this . props
37
+ onSchemaSelect = ( e ) => {
38
+ const selectedSchema = e . target . value
39
+ const schemaPath = [ "components" , "schemas" , selectedSchema ]
40
+
41
+ const isResolved = this . props . specSelectors . specResolvedSubtree ( schemaPath ) != null
42
+ if ( ! isResolved ) {
43
+ this . props . specActions . requestResolvedSubtree ( schemaPath )
44
+ }
45
+
46
+ this . setState ( { selectedSchema } )
47
+ }
48
+
49
+ decodeRefName = ( uri ) => {
50
+ const unescaped = uri . replace ( / ~ 1 / g, "/" ) . replace ( / ~ 0 / g, "~" )
51
+ try {
52
+ return decodeURIComponent ( unescaped )
53
+ } catch {
54
+ return unescaped
55
+ }
56
+ }
57
+
58
+ getModelName = ( uri ) => {
59
+ if ( typeof uri === "string" && uri . includes ( "#/components/schemas/" ) ) {
60
+ return this . decodeRefName ( uri . replace ( / ^ .* # \/ c o m p o n e n t s \/ s c h e m a s \/ / , "" ) )
61
+ }
62
+ return null
63
+ }
64
+
65
+ /**
66
+ * Builds a Map of schema options combining explicit discriminator mappings and implicit mappings.
67
+ *
68
+ * @returns {Map<string, string[]> } A Map where:
69
+ * - key: the schema name (e.g., "Cat", "Dog")
70
+ * - value: array of discriminator values that map to this schema
71
+ *
72
+ * Examples:
73
+ * 1. Explicit mapping only:
74
+ * { "Cat": ["kitty", "kitten"], "Dog": ["puppy"] }
75
+ *
76
+ * 2. Implicit mapping only:
77
+ * { "Cat": ["Cat"], "Dog": ["Dog"] }
78
+ *
79
+ * 3. Mixed mapping:
80
+ * { "Cat": ["kitty", "kitten"], "Dog": ["Dog"] }
81
+ * where "Cat" has explicit mappings but "Dog" uses implicit
82
+ */
83
+ buildSchemaOptions = ( name , discriminator , schemaMap ) => {
84
+ const options = new Map ( )
85
+ const mapping = discriminator && discriminator . get ( "mapping" )
86
+
87
+ // First add any explicit mappings
88
+ if ( mapping && mapping . size > 0 ) {
89
+ mapping . forEach ( ( schemaRef , key ) => {
90
+ const schemaName = this . getModelName ( schemaRef )
91
+ if ( schemaName ) {
92
+ const existing = options . get ( schemaName ) || [ ]
93
+ options . set ( schemaName , [ ...existing , key ] )
94
+ }
95
+ } )
96
+ }
97
+
98
+ // Then add implicit mappings for any schemas not already mapped
99
+ const childSchemas = schemaMap [ name ] || [ ]
100
+ childSchemas . forEach ( childName => {
101
+ if ( ! options . has ( childName ) ) {
102
+ // No explicit mapping for this schema, use implicit
103
+ options . set ( childName , [ childName ] )
104
+ }
105
+ } )
106
+
107
+ return options
108
+ }
109
+
110
+ render ( ) {
111
+ let { getComponent, getConfigs, schema, specSelectors } = this . props
32
112
const Model = getComponent ( "Model" )
33
113
34
114
let expanded
35
115
if ( this . props . layoutSelectors ) {
36
- // If this is prop is present, we'll have deepLinking for it
37
116
expanded = this . props . layoutSelectors . isShown ( this . props . fullPath )
38
117
}
39
118
40
- return < div className = "model-box" >
41
- < Model { ...this . props } getConfigs = { getConfigs } expanded = { expanded } depth = { 1 } onToggle = { this . onToggle } expandDepth = { this . props . expandDepth || 0 } />
42
- </ div >
119
+ const name = this . getModelName ( schema . get ( "$$ref" ) )
120
+ const schemaMap = specSelectors . getParentToChildMap ( )
121
+ const discriminator = schema . get ( "discriminator" )
122
+
123
+ const options = this . buildSchemaOptions ( name , discriminator , schemaMap )
124
+ const showDropdown = ! ! discriminator && options . size > 0
125
+
126
+ // Use selected schema or original base schema
127
+ const effectiveSchema = this . state . selectedSchema
128
+ ? specSelectors . findDefinition ( this . state . selectedSchema )
129
+ : schema
130
+
131
+ return (
132
+ < div className = "model-box" >
133
+ { showDropdown && (
134
+ < div className = "model-box-control" >
135
+ < select onChange = { this . onSchemaSelect } value = { this . state . selectedSchema || "" } >
136
+ < option value = "" > Base: { name } </ option >
137
+ { Array . from ( options . entries ( ) ) . map ( ( [ schemaName , keys ] ) => (
138
+ < option key = { schemaName } value = { schemaName } >
139
+ { keys . length > 1 ? `${ keys . join ( " | " ) } (${ schemaName } )` : schemaName }
140
+ </ option >
141
+ ) ) }
142
+ </ select >
143
+ </ div >
144
+ ) }
145
+ < Model
146
+ { ...this . props }
147
+ name = { this . state . selectedSchema }
148
+ schema = { effectiveSchema }
149
+ getConfigs = { getConfigs }
150
+ expanded = { expanded }
151
+ depth = { 1 }
152
+ onToggle = { this . onToggle }
153
+ expandDepth = { this . props . expandDepth || 0 }
154
+ />
155
+ </ div >
156
+ )
43
157
}
44
158
}
0 commit comments