瀏覽代碼

layout tree crawling

Daniel Zlotin 8 年之前
父節點
當前提交
df6729aa9c

+ 7
- 1
playground/e2e/app.test.js 查看文件

@@ -12,9 +12,15 @@ describe('app', () => {
12 12
     expect(elementByLabel('This is tab 1')).toBeVisible();
13 13
   });
14 14
 
15
+  it('passing props and functions', () => {
16
+    elementByLabel('Switch to tab based app').tap();
17
+    expect(elementByLabel('Hello from a function!')).toBeVisible();
18
+  });
19
+
15 20
   xit('switch to tabs with side menus', () => {
16 21
     elementByLabel('Switch to tab based app with side menus').tap();
17
-    // expect(elementByLabel('This is a tab screen')).toBeVisible();
22
+    elementByLabel('Switch to tab based app with side menus').swipeRight();
23
+    expect(elementByLabel('This is a side menu screen')).toBeVisible();
18 24
   });
19 25
 });
20 26
 

+ 1
- 1
playground/package.json 查看文件

@@ -4,7 +4,7 @@
4 4
   "private": true,
5 5
   "scripts": {
6 6
     "quickinstall": "node ./scripts/quickInstall.js",
7
-    "start": "adb reverse tcp:8081 tcp:8081; watchman watch-del-all; react-native start --reset-cache",
7
+    "start": "adb reverse tcp:8081 tcp:8081; watchman watch-del-all; react-native start",
8 8
     "xcode": "open ios/playground.xcodeproj",
9 9
     "android": "cd android && ./gradlew installDebug",
10 10
     "e2e": "node ./scripts/e2e.ios.js"

+ 11
- 2
playground/src/containers/SimpleScreen.js 查看文件

@@ -3,13 +3,22 @@ import { View, Text } from 'react-native';
3 3
 
4 4
 class SimpleScreen extends Component {
5 5
   render() {
6
-    const text = this.props.text || 'Simple Screen';
7 6
     return (
8 7
       <View style={styles.root}>
9
-        <Text style={styles.h1}>{text}</Text>
8
+        <Text style={styles.h1}>{this.props.text || 'Simple Screen'}</Text>
9
+        {this.renderTextFromFunctionInProps()}
10 10
       </View>
11 11
     );
12 12
   }
13
+
14
+  renderTextFromFunctionInProps() {
15
+    if (!this.props.myFunction) {
16
+      return undefined;
17
+    }
18
+    return (
19
+      <Text style={styles.h1}>{this.props.myFunction()}</Text>
20
+    );
21
+  }
13 22
 }
14 23
 export default SimpleScreen;
15 24
 

+ 2
- 1
playground/src/containers/WelcomeScreen.js 查看文件

@@ -25,7 +25,8 @@ class WelcomeScreen extends Component {
25 25
           container: {
26 26
             name: 'com.example.SimpleScreen',
27 27
             passProps: {
28
-              text: 'This is tab 1'
28
+              text: 'This is tab 1',
29
+              myFunction: () => 'Hello from a function!'
29 30
             }
30 31
           }
31 32
         },

+ 8
- 12
src/commands/Commands.js 查看文件

@@ -1,23 +1,19 @@
1
-import * as _ from 'lodash';
1
+import _ from 'lodash';
2 2
 import LayoutTreeParser from './LayoutTreeParser';
3
+import LayoutTreeCrawler from './LayoutTreeCrawler';
3 4
 
4 5
 export default class Commands {
5 6
   constructor(nativeCommandsSender, uniqueIdProvider, store) {
6 7
     this.nativeCommandsSender = nativeCommandsSender;
7
-    this.layoutTreeParser = new LayoutTreeParser(uniqueIdProvider);
8
-    this.store = store;
8
+    this.uniqueIdProvider = uniqueIdProvider;
9
+    this.layoutTreeParser = new LayoutTreeParser();
10
+    this.layoutTreeCrawler = new LayoutTreeCrawler(uniqueIdProvider, store);
9 11
   }
10 12
 
11 13
   setRoot(simpleApi) {
12
-    const layout = this.layoutTreeParser.parseFromSimpleJSON(simpleApi);
13
-    this._savePassProps(layout);
14
+    const input = _.cloneDeep(simpleApi);
15
+    const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
16
+    this.layoutTreeCrawler.crawl(layout);
14 17
     this.nativeCommandsSender.setRoot(layout);
15 18
   }
16
-
17
-  _savePassProps(node) {
18
-    if (node.type === 'Container') {
19
-      this.store.setPropsForContainerId(node.id, node.data.passProps);
20
-    }
21
-    _.forEach(node.children, (child) => this._savePassProps(child));
22
-  }
23 19
 }

+ 9
- 1
src/commands/Commands.test.js 查看文件

@@ -8,7 +8,9 @@ describe('Commands', () => {
8 8
   beforeEach(() => {
9 9
     const NativeCommandsSender = jest.genMockFromModule('../adapters/NativeCommandsSender').default;
10 10
     mockCommandsSender = new NativeCommandsSender();
11
+
11 12
     const mockIdProvider = { generate: (prefix) => `${prefix}+UNIQUE_ID` };
13
+
12 14
     const Store = require('../containers/Store').default;
13 15
     store = new Store();
14 16
 
@@ -17,7 +19,7 @@ describe('Commands', () => {
17 19
   });
18 20
 
19 21
   describe('setRoot', () => {
20
-    it('sends setRoot to native after parsing into layoutTree', () => {
22
+    it('sends setRoot to native after parsing into a correct layout tree', () => {
21 23
       uut.setRoot({
22 24
         container: {
23 25
           name: 'com.example.MyScreen'
@@ -41,6 +43,12 @@ describe('Commands', () => {
41 43
       });
42 44
     });
43 45
 
46
+    it('deep clones input to avoid mutation errors', () => {
47
+      const obj = {};
48
+      uut.setRoot({ container: { inner: obj } });
49
+      expect(mockCommandsSender.setRoot.mock.calls[0][0].children[0].data.inner).not.toBe(obj);
50
+    });
51
+
44 52
     it('passProps into containers', () => {
45 53
       expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({});
46 54
       uut.setRoot(SimpleLayouts.singleScreenWithAditionalParams);

+ 27
- 0
src/commands/LayoutTreeCrawler.js 查看文件

@@ -0,0 +1,27 @@
1
+import _ from 'lodash';
2
+import LayoutTypes from './LayoutTypes';
3
+
4
+export default class LayoutTreeCrawler {
5
+  constructor(uniqueIdProvider, store) {
6
+    this.uniqueIdProvider = uniqueIdProvider;
7
+    this.store = store;
8
+    this.crawl = this.crawl.bind(this);
9
+  }
10
+
11
+  crawl(node) {
12
+    this._assertKnownLayoutType(node.type);
13
+    node.id = this.uniqueIdProvider.generate(node.type);
14
+    node.data = node.data || {};
15
+    node.children = node.children || [];
16
+    if (node.type === LayoutTypes.Container) {
17
+      this.store.setPropsForContainerId(node.id, node.data.passProps);
18
+    }
19
+    _.forEach(node.children, this.crawl);
20
+  }
21
+
22
+  _assertKnownLayoutType(type) {
23
+    if (!_.includes(LayoutTypes, type)) {
24
+      throw new Error(`Unknown layout type ${type}`);
25
+    }
26
+  }
27
+}

+ 47
- 0
src/commands/LayoutTreeCrawler.test.js 查看文件

@@ -0,0 +1,47 @@
1
+import LayoutTypes from './LayoutTypes';
2
+
3
+describe('LayoutTreeCrawler', () => {
4
+  let uut;
5
+  let store;
6
+
7
+  beforeEach(() => {
8
+    const LayoutTreeCrawler = require('./LayoutTreeCrawler').default;
9
+    const uniqueIdProvider = { generate: (prefix) => `${prefix}+UNIQUE_ID` };
10
+    const Store = require('../containers/Store').default;
11
+    store = new Store();
12
+
13
+    uut = new LayoutTreeCrawler(uniqueIdProvider, store);
14
+  });
15
+
16
+  it('crawls a layout tree and adds unique id to each node', () => {
17
+    const node = { type: LayoutTypes.Container, children: [{ type: LayoutTypes.Tabs }] };
18
+    uut.crawl(node);
19
+    expect(node.id).toEqual('Container+UNIQUE_ID');
20
+    expect(node.children[0].id).toEqual('Tabs+UNIQUE_ID');
21
+  });
22
+
23
+  it('crawls a layout tree and ensures data exists', () => {
24
+    const node = { type: LayoutTypes.Container, children: [{ type: LayoutTypes.Tabs }] };
25
+    uut.crawl(node);
26
+    expect(node.data).toEqual({});
27
+    expect(node.children[0].data).toEqual({});
28
+  });
29
+
30
+  it('crawls a layout tree and ensures children exists', () => {
31
+    const node = { type: LayoutTypes.Container, children: [{ type: LayoutTypes.Tabs }] };
32
+    uut.crawl(node);
33
+    expect(node.children[0].children).toEqual([]);
34
+  });
35
+
36
+  it('crawls a layout tree and asserts known layout type', () => {
37
+    const node = { type: LayoutTypes.Container, children: [{ type: 'Bob' }] };
38
+    expect(() => uut.crawl(node)).toThrow(new Error('Unknown layout type Bob'));
39
+  });
40
+
41
+  it('saves passProps into store for Container nodes', () => {
42
+    const node = { type: LayoutTypes.Tabs, children: [{ type: LayoutTypes.Container, data: { passProps: { myProp: 123 } } }] };
43
+    expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({});
44
+    uut.crawl(node);
45
+    expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({ myProp: 123 });
46
+  });
47
+});

+ 32
- 49
src/commands/LayoutTreeParser.js 查看文件

@@ -1,85 +1,68 @@
1 1
 import * as _ from 'lodash';
2
-
3
-const Types = {
4
-  Container: 'Container',
5
-  ContainerStack: 'ContainerStack',
6
-  Tabs: 'Tabs',
7
-  SideMenuRoot: 'SideMenuRoot',
8
-  SideMenuCenter: 'SideMenuCenter',
9
-  SideMenuLeft: 'SideMenuLeft',
10
-  SideMenuRight: 'SideMenuRight'
11
-};
2
+import LayoutTypes from './LayoutTypes';
12 3
 
13 4
 export default class LayoutTreeParser {
14
-  constructor(uniqueIdProvider) {
15
-    this.uniqueIdProvider = uniqueIdProvider;
16
-  }
17
-
5
+  /**
6
+   * returns correct layout tree of {type, children, data?}
7
+   */
18 8
   parseFromSimpleJSON(simpleJsonApi) {
19
-    const input = _.cloneDeep(simpleJsonApi);
20
-    if (input.sideMenu) {
21
-      return this._createSideMenu(input);
9
+    if (simpleJsonApi.sideMenu) {
10
+      return this._createSideMenu(simpleJsonApi);
22 11
     }
23
-    if (input.tabs) {
24
-      return this._createTabs(input.tabs);
12
+    if (simpleJsonApi.tabs) {
13
+      return this._createTabs(simpleJsonApi.tabs);
25 14
     }
26
-    return this._createContainerStackWithContainer(input.container);
27
-  }
28
-
29
-  _node(node) {
30
-    node.id = this.uniqueIdProvider.generate(node.type);
31
-    node.children = node.children || [];
32
-    node.data = node.data || {};
33
-    return node;
15
+    return this._createContainerStackWithContainer(simpleJsonApi.container);
34 16
   }
35 17
 
36 18
   _createTabs(tabs) {
37
-    return this._node({
38
-      type: Types.Tabs,
19
+    return {
20
+      type: LayoutTypes.Tabs,
39 21
       children: _.map(tabs, (t) => this._createContainerStackWithContainer(t.container))
40
-    });
22
+    };
41 23
   }
42 24
 
43 25
   _createContainerStackWithContainer(container) {
44
-    return this._node({
45
-      type: Types.ContainerStack,
26
+    return {
27
+      type: LayoutTypes.ContainerStack,
46 28
       children: [this._createContainer(container)]
47
-    });
29
+    };
48 30
   }
49 31
 
50 32
   _createContainer(container) {
51
-    return this._node({
52
-      type: Types.Container,
53
-      data: container
54
-    });
33
+    return {
34
+      type: LayoutTypes.Container,
35
+      data: container,
36
+      children: []
37
+    };
55 38
   }
56 39
 
57 40
   _createSideMenu(layout) {
58
-    return this._node({
59
-      type: Types.SideMenuRoot,
41
+    return {
42
+      type: LayoutTypes.SideMenuRoot,
60 43
       children: this._createSideMenuChildren(layout)
61
-    });
44
+    };
62 45
   }
63 46
 
64 47
   _createSideMenuChildren(layout) {
65 48
     const children = [];
66 49
     if (layout.sideMenu.left) {
67
-      children.push(this._node({
68
-        type: Types.SideMenuLeft,
50
+      children.push({
51
+        type: LayoutTypes.SideMenuLeft,
69 52
         children: [this._createContainer(layout.sideMenu.left.container)]
70
-      }));
53
+      });
71 54
     }
72
-    children.push(this._node({
73
-      type: Types.SideMenuCenter,
55
+    children.push({
56
+      type: LayoutTypes.SideMenuCenter,
74 57
       children: [
75 58
         layout.tabs ? this._createTabs(layout.tabs) : this._createContainerStackWithContainer(layout.container)
76 59
       ]
77
-    }));
60
+    });
78 61
     if (layout.sideMenu.right) {
79
-      children.push(this._node({
80
-        type: Types.SideMenuRight,
62
+      children.push({
63
+        type: LayoutTypes.SideMenuRight,
81 64
         children: [this._createContainer(layout.sideMenu.right.container)]
82
-      }));
65
+      });
83 66
     }
84 67
     return children;
85 68
   }

+ 1
- 94
src/commands/LayoutTreeParser.test.js 查看文件

@@ -4,45 +4,17 @@ describe('LayoutTreeParser', () => {
4 4
   let uut;
5 5
 
6 6
   beforeEach(() => {
7
-    const uniqueIdProvider = { generate: (prefix) => `${prefix}+UNIQUE_ID` };
8 7
     const LayoutTreeParser = require('./LayoutTreeParser').default;
9
-    uut = new LayoutTreeParser(uniqueIdProvider);
10
-  });
11
-
12
-  it('adds uniqueId to containers', () => {
13
-    const input = { container: {} };
14
-    expect(uut.parseFromSimpleJSON(input))
15
-      .toEqual({
16
-        type: 'ContainerStack',
17
-        id: 'ContainerStack+UNIQUE_ID',
18
-        children: [
19
-          {
20
-            type: 'Container',
21
-            id: 'Container+UNIQUE_ID',
22
-            data: {},
23
-            children: []
24
-          }
25
-        ],
26
-        data: {}
27
-      });
28
-  });
29
-
30
-  it('deep clones to avoid mutations', () => {
31
-    const obj = {};
32
-    const result = uut.parseFromSimpleJSON({ container: { foo: obj } });
33
-    expect(result.children[0].data.foo).not.toBe(obj);
8
+    uut = new LayoutTreeParser();
34 9
   });
35 10
 
36 11
   it('parses single screen', () => {
37 12
     expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenApp))
38 13
       .toEqual({
39 14
         type: 'ContainerStack',
40
-        id: 'ContainerStack+UNIQUE_ID',
41
-        data: {},
42 15
         children: [
43 16
           {
44 17
             type: 'Container',
45
-            id: 'Container+UNIQUE_ID',
46 18
             data: {
47 19
               name: 'com.example.MyScreen'
48 20
             },
@@ -56,12 +28,9 @@ describe('LayoutTreeParser', () => {
56 28
     expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenWithAditionalParams))
57 29
       .toEqual({
58 30
         type: 'ContainerStack',
59
-        id: 'ContainerStack+UNIQUE_ID',
60
-        data: {},
61 31
         children: [
62 32
           {
63 33
             type: 'Container',
64
-            id: 'Container+UNIQUE_ID',
65 34
             children: [],
66 35
             data: {
67 36
               name: 'com.example.MyScreen',
@@ -82,17 +51,12 @@ describe('LayoutTreeParser', () => {
82 51
     expect(uut.parseFromSimpleJSON(SimpleLayouts.tabBasedApp))
83 52
       .toEqual({
84 53
         type: 'Tabs',
85
-        id: 'Tabs+UNIQUE_ID',
86
-        data: {},
87 54
         children: [
88 55
           {
89 56
             type: 'ContainerStack',
90
-            id: 'ContainerStack+UNIQUE_ID',
91
-            data: {},
92 57
             children: [
93 58
               {
94 59
                 type: 'Container',
95
-                id: 'Container+UNIQUE_ID',
96 60
                 children: [],
97 61
                 data: {
98 62
                   name: 'com.example.ATab'
@@ -102,12 +66,9 @@ describe('LayoutTreeParser', () => {
102 66
           },
103 67
           {
104 68
             type: 'ContainerStack',
105
-            id: 'ContainerStack+UNIQUE_ID',
106
-            data: {},
107 69
             children: [
108 70
               {
109 71
                 type: 'Container',
110
-                id: 'Container+UNIQUE_ID',
111 72
                 children: [],
112 73
                 data: {
113 74
                   name: 'com.example.SecondTab'
@@ -117,12 +78,9 @@ describe('LayoutTreeParser', () => {
117 78
           },
118 79
           {
119 80
             type: 'ContainerStack',
120
-            id: 'ContainerStack+UNIQUE_ID',
121
-            data: {},
122 81
             children: [
123 82
               {
124 83
                 type: 'Container',
125
-                id: 'Container+UNIQUE_ID',
126 84
                 children: [],
127 85
                 data: {
128 86
                   name: 'com.example.ATab'
@@ -138,17 +96,12 @@ describe('LayoutTreeParser', () => {
138 96
     expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithSideMenu))
139 97
       .toEqual({
140 98
         type: 'SideMenuRoot',
141
-        id: 'SideMenuRoot+UNIQUE_ID',
142
-        data: {},
143 99
         children: [
144 100
           {
145 101
             type: 'SideMenuLeft',
146
-            id: 'SideMenuLeft+UNIQUE_ID',
147
-            data: {},
148 102
             children: [
149 103
               {
150 104
                 type: 'Container',
151
-                id: 'Container+UNIQUE_ID',
152 105
                 data: {
153 106
                   name: 'com.example.SideMenu'
154 107
                 },
@@ -158,17 +111,12 @@ describe('LayoutTreeParser', () => {
158 111
           },
159 112
           {
160 113
             type: 'SideMenuCenter',
161
-            id: 'SideMenuCenter+UNIQUE_ID',
162
-            data: {},
163 114
             children: [
164 115
               {
165 116
                 type: 'ContainerStack',
166
-                id: 'ContainerStack+UNIQUE_ID',
167
-                data: {},
168 117
                 children: [
169 118
                   {
170 119
                     type: 'Container',
171
-                    id: 'Container+UNIQUE_ID',
172 120
                     data: {
173 121
                       name: 'com.example.MyScreen'
174 122
                     },
@@ -186,22 +134,15 @@ describe('LayoutTreeParser', () => {
186 134
     expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithRightSideMenu))
187 135
       .toEqual({
188 136
         type: 'SideMenuRoot',
189
-        id: 'SideMenuRoot+UNIQUE_ID',
190
-        data: {},
191 137
         children: [
192 138
           {
193 139
             type: 'SideMenuCenter',
194
-            id: 'SideMenuCenter+UNIQUE_ID',
195
-            data: {},
196 140
             children: [
197 141
               {
198 142
                 type: 'ContainerStack',
199
-                id: 'ContainerStack+UNIQUE_ID',
200
-                data: {},
201 143
                 children: [
202 144
                   {
203 145
                     type: 'Container',
204
-                    id: 'Container+UNIQUE_ID',
205 146
                     data: {
206 147
                       name: 'com.example.MyScreen'
207 148
                     },
@@ -213,12 +154,9 @@ describe('LayoutTreeParser', () => {
213 154
           },
214 155
           {
215 156
             type: 'SideMenuRight',
216
-            id: 'SideMenuRight+UNIQUE_ID',
217
-            data: {},
218 157
             children: [
219 158
               {
220 159
                 type: 'Container',
221
-                id: 'Container+UNIQUE_ID',
222 160
                 data: {
223 161
                   name: 'com.example.SideMenu'
224 162
                 },
@@ -234,17 +172,12 @@ describe('LayoutTreeParser', () => {
234 172
     expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithBothMenus))
235 173
       .toEqual({
236 174
         type: 'SideMenuRoot',
237
-        id: 'SideMenuRoot+UNIQUE_ID',
238
-        data: {},
239 175
         children: [
240 176
           {
241 177
             type: 'SideMenuLeft',
242
-            id: 'SideMenuLeft+UNIQUE_ID',
243
-            data: {},
244 178
             children: [
245 179
               {
246 180
                 type: 'Container',
247
-                id: 'Container+UNIQUE_ID',
248 181
                 data: {
249 182
                   name: 'com.example.Menu1'
250 183
                 },
@@ -254,17 +187,12 @@ describe('LayoutTreeParser', () => {
254 187
           },
255 188
           {
256 189
             type: 'SideMenuCenter',
257
-            id: 'SideMenuCenter+UNIQUE_ID',
258
-            data: {},
259 190
             children: [
260 191
               {
261 192
                 type: 'ContainerStack',
262
-                id: 'ContainerStack+UNIQUE_ID',
263
-                data: {},
264 193
                 children: [
265 194
                   {
266 195
                     type: 'Container',
267
-                    id: 'Container+UNIQUE_ID',
268 196
                     data: {
269 197
                       name: 'com.example.MyScreen'
270 198
                     },
@@ -276,12 +204,9 @@ describe('LayoutTreeParser', () => {
276 204
           },
277 205
           {
278 206
             type: 'SideMenuRight',
279
-            id: 'SideMenuRight+UNIQUE_ID',
280
-            data: {},
281 207
             children: [
282 208
               {
283 209
                 type: 'Container',
284
-                id: 'Container+UNIQUE_ID',
285 210
                 data: {
286 211
                   name: 'com.example.Menu2'
287 212
                 },
@@ -297,17 +222,12 @@ describe('LayoutTreeParser', () => {
297 222
     expect(uut.parseFromSimpleJSON(SimpleLayouts.tabBasedWithBothSideMenus))
298 223
       .toEqual({
299 224
         type: 'SideMenuRoot',
300
-        id: 'SideMenuRoot+UNIQUE_ID',
301
-        data: {},
302 225
         children: [
303 226
           {
304 227
             type: 'SideMenuLeft',
305
-            id: 'SideMenuLeft+UNIQUE_ID',
306
-            data: {},
307 228
             children: [
308 229
               {
309 230
                 type: 'Container',
310
-                id: 'Container+UNIQUE_ID',
311 231
                 data: {
312 232
                   name: 'com.example.Menu1'
313 233
                 },
@@ -317,22 +237,15 @@ describe('LayoutTreeParser', () => {
317 237
           },
318 238
           {
319 239
             type: 'SideMenuCenter',
320
-            id: 'SideMenuCenter+UNIQUE_ID',
321
-            data: {},
322 240
             children: [
323 241
               {
324 242
                 type: 'Tabs',
325
-                id: 'Tabs+UNIQUE_ID',
326
-                data: {},
327 243
                 children: [
328 244
                   {
329 245
                     type: 'ContainerStack',
330
-                    id: 'ContainerStack+UNIQUE_ID',
331
-                    data: {},
332 246
                     children: [
333 247
                       {
334 248
                         type: 'Container',
335
-                        id: 'Container+UNIQUE_ID',
336 249
                         data: {
337 250
                           name: 'com.example.FirstTab'
338 251
                         },
@@ -342,12 +255,9 @@ describe('LayoutTreeParser', () => {
342 255
                   },
343 256
                   {
344 257
                     type: 'ContainerStack',
345
-                    id: 'ContainerStack+UNIQUE_ID',
346
-                    data: {},
347 258
                     children: [
348 259
                       {
349 260
                         type: 'Container',
350
-                        id: 'Container+UNIQUE_ID',
351 261
                         data: {
352 262
                           name: 'com.example.SecondTab'
353 263
                         },
@@ -361,12 +271,9 @@ describe('LayoutTreeParser', () => {
361 271
           },
362 272
           {
363 273
             type: 'SideMenuRight',
364
-            id: 'SideMenuRight+UNIQUE_ID',
365
-            data: {},
366 274
             children: [
367 275
               {
368 276
                 type: 'Container',
369
-                id: 'Container+UNIQUE_ID',
370 277
                 data: {
371 278
                   name: 'com.example.Menu2'
372 279
                 },

+ 9
- 0
src/commands/LayoutTypes.js 查看文件

@@ -0,0 +1,9 @@
1
+export default {
2
+  Container: 'Container',
3
+  ContainerStack: 'ContainerStack',
4
+  Tabs: 'Tabs',
5
+  SideMenuRoot: 'SideMenuRoot',
6
+  SideMenuCenter: 'SideMenuCenter',
7
+  SideMenuLeft: 'SideMenuLeft',
8
+  SideMenuRight: 'SideMenuRight'
9
+};