Skip to content

Commit b5ece97

Browse files
committed
feat: on collapse rendering
1 parent 59b51b6 commit b5ece97

File tree

9 files changed

+94
-40
lines changed

9 files changed

+94
-40
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"dependencies": {
4646
"@stoplight/json": "^3.5.1",
4747
"@stoplight/react-error-boundary": "^1.0.0",
48+
"@stoplight/tree-list": "file:.yalc/@stoplight/tree-list",
4849
"classnames": "^2.2.6",
4950
"json-schema-merge-allof": "https://github.com/stoplightio/json-schema-merge-allof",
5051
"lodash-es": "^4.17.15",
@@ -59,7 +60,6 @@
5960
"@stoplight/markdown-viewer": "^3.5.5",
6061
"@stoplight/scripts": "7.0.4",
6162
"@stoplight/storybook-config": "^2.0.4",
62-
"@stoplight/tree-list": "file:.yalc/@stoplight/tree-list",
6363
"@stoplight/types": "11.0.0",
6464
"@stoplight/ui-kit": "^2.10.0",
6565
"@types/classnames": "^2.2.9",

src/__stories__/JsonSchemaViewer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ storiesOf('JsonSchemaViewer', module)
2323
<JsonSchemaViewer
2424
name={text('name', 'my schema')}
2525
schema={schema as JSONSchema4}
26-
defaultExpandedDepth={number('defaultExpandedDepth', 2)}
26+
defaultExpandedDepth={number('defaultExpandedDepth', 0)}
2727
expanded={boolean('expanded', false)}
2828
hideTopBar={boolean('hideTopBar', false)}
2929
onGoToRef={action('onGoToRef')}

src/components/JsonSchemaViewer.tsx

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import { ErrorBoundaryForwardedProps, FallbackComponent, withErrorBoundary } from '@stoplight/react-error-boundary';
2-
import { Tree, TreeState, TreeStore } from '@stoplight/tree-list';
2+
import { TreeState, TreeStore } from '@stoplight/tree-list';
33
import { Intent, Spinner } from '@stoplight/ui-kit';
44
import cn from 'classnames';
55
import { action, runInAction } from 'mobx';
66
import * as React from 'react';
7-
import SchemaWorker, { WebWorker } from 'web-worker:../workers/schema.ts';
87

98
import { JSONSchema4 } from 'json-schema';
10-
import { populateTree } from '../tree/populateTree';
9+
import { SchemaTree } from '../tree/tree';
1110
import { GoToRefHandler, RowRenderer } from '../types';
1211
import { isSchemaViewerEmpty } from '../utils/isSchemaViewerEmpty';
1312
import { ComputeSchemaMessageData, isRenderedSchemaMessage } from '../workers/messages';
14-
import { SchemaTree } from './SchemaTree';
13+
import { SchemaTree as SchemaTreeComponent } from './SchemaTree';
1514

1615
export interface IJsonSchemaViewer {
1716
schema: JSONSchema4;
@@ -32,9 +31,10 @@ export interface IJsonSchemaViewer {
3231

3332
export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaViewer & ErrorBoundaryForwardedProps> {
3433
protected readonly treeStore: TreeStore;
35-
protected readonly tree: Tree;
34+
protected readonly tree: SchemaTree;
35+
protected readonly treeState: TreeState;
3636
protected readonly instanceId: string;
37-
public static schemaWorker?: WebWorker;
37+
// public static schemaWorker?: WebWorker;
3838

3939
public state = {
4040
computing: false,
@@ -43,8 +43,9 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
4343
constructor(props: IJsonSchemaViewer & ErrorBoundaryForwardedProps) {
4444
super(props);
4545

46-
this.tree = new Tree(Tree.createArtificialRoot());
47-
this.treeStore = new TreeStore(this.tree, new TreeState(), {
46+
this.treeState = new TreeState();
47+
this.tree = new SchemaTree(this.schema, this.treeState, this.expandedDepth);
48+
this.treeStore = new TreeStore(this.tree, this.treeState, {
4849
defaultExpandedDepth: this.expandedDepth,
4950
});
5051

@@ -130,35 +131,33 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
130131
// }
131132

132133
protected renderSchema() {
133-
populateTree(this.schema, this.tree.root, 0, [], null);
134-
this.tree.root = this.tree.root.children[0];
135-
this.tree.invalidate();
134+
this.tree.populate();
136135
// const needsFullRendering = true;
137136
// this.prerenderSchema();
138137
// if (!needsFullRendering) {
139138
// return;
140139
// }
141140

142-
const message: ComputeSchemaMessageData = {
143-
instanceId: this.instanceId,
144-
schema: this.schema,
145-
mergeAllOf: this.props.mergeAllOf !== false,
146-
};
147-
148-
if (JsonSchemaViewerComponent.schemaWorker !== void 0) {
149-
JsonSchemaViewerComponent.schemaWorker.postMessage(message);
150-
}
141+
// const message: ComputeSchemaMessageData = {
142+
// instanceId: this.instanceId,
143+
// schema: this.schema,
144+
// mergeAllOf: this.props.mergeAllOf !== false,
145+
// };
146+
//
147+
// if (JsonSchemaViewerComponent.schemaWorker !== void 0) {
148+
// JsonSchemaViewerComponent.schemaWorker.postMessage(message);
149+
// }
151150

152151
this.setState({ computing: false });
153152
}
154153

155154
public componentDidMount() {
156155
// let's initialize it lazily
157-
if (JsonSchemaViewerComponent.schemaWorker === void 0) {
158-
JsonSchemaViewerComponent.schemaWorker = new SchemaWorker();
159-
}
156+
// if (JsonSchemaViewerComponent.schemaWorker === void 0) {
157+
// JsonSchemaViewerComponent.schemaWorker = new SchemaWorker();
158+
// }
160159

161-
JsonSchemaViewerComponent.schemaWorker.addEventListener('message', this.handleWorkerMessage);
160+
// JsonSchemaViewerComponent.schemaWorker.addEventListener('message', this.handleWorkerMessage);
162161
this.renderSchema();
163162
}
164163

@@ -197,7 +196,7 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
197196
<Spinner className="relative" intent={Intent.PRIMARY} size={Spinner.SIZE_LARGE} />
198197
</div>
199198
)}
200-
<SchemaTree expanded={expanded} name={name} schema={schema} treeStore={this.treeStore} {...props} />
199+
<SchemaTreeComponent expanded={expanded} name={name} schema={schema} treeStore={this.treeStore} {...props} />
201200
</div>
202201
);
203202
}

src/components/SchemaTree.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as React from 'react';
55

66
import { GoToRefHandler, RowRenderer } from '../types';
77
import { SchemaRow } from './SchemaRow';
8+
import { MetadataStore } from '../tree/metadata';
89

910
export interface ISchemaTree {
1011
treeStore: TreeStore;

src/tree/metadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ export interface ITreeNodeMeta {
66
schema: SchemaNode;
77
}
88

9-
export const MetadataStore: Dictionary<ITreeNodeMeta, string> = {};
9+
export const MetadataStore: Dictionary<ITreeNodeMeta, string> = {}; // todo: can be a weakmap I guess since no stringyfing is needed

src/tree/populateTree.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { isLocalRef } from '@stoplight/json';
22
import { TreeListNode, TreeListParentNode } from '@stoplight/tree-list';
33
import { JsonPath } from '@stoplight/types';
44
import { JSONSchema4 } from 'json-schema';
5-
import { IArrayNode, IObjectNode, SchemaKind, SchemaTreeListNode } from '../types';
5+
import { IArrayNode, IObjectNode, SchemaKind, SchemaNode, SchemaTreeListNode } from '../types';
66
import { getPrimaryType } from '../utils/getPrimaryType';
77
import { isCombinerNode, isRefNode } from '../utils/guards';
88
import { isCombiner } from '../utils/isCombiner';
99
import { MetadataStore } from './metadata';
1010
import { walk } from './walk';
1111

1212
export type WalkingOptions = {
13-
depth?: number;
13+
onNode?(node: SchemaNode, parentTreeNode: TreeListNode, level: number): boolean | void;
1414
};
1515

1616
export type Walker = (
@@ -23,9 +23,10 @@ export type Walker = (
2323

2424
export const populateTree: Walker = (schema, parent, level, path, options) => {
2525
if (typeof schema !== 'object' || schema === null) return;
26-
if (options !== null && options.depth !== void 0 && level >= options.depth) return;
2726

2827
for (const node of walk(schema)) {
28+
if (options !== null && options.onNode !== void 0 && !options.onNode(node, parent, level)) continue;
29+
2930
const treeNode: SchemaTreeListNode = {
3031
id: node.id,
3132
name: '',
@@ -77,14 +78,14 @@ export const populateTree: Walker = (schema, parent, level, path, options) => {
7778
};
7879

7980
function processArray(
80-
node: TreeListNode,
81+
node: SchemaTreeListNode,
8182
schema: IArrayNode,
8283
level: number,
8384
path: JsonPath,
8485
options: WalkingOptions | null,
85-
): TreeListNode {
86+
): SchemaTreeListNode {
8687
if (Array.isArray(schema.items)) {
87-
const children: TreeListNode[] = [];
88+
const children: SchemaTreeListNode[] = [];
8889
(node as TreeListParentNode).children = children;
8990
for (const [i, property] of schema.items.entries()) {
9091
const child = populateTree(property, node as TreeListParentNode, level + 1, [...path, 'items', i], options);
@@ -96,12 +97,12 @@ function processArray(
9697
const subtype = getPrimaryType(schema.items);
9798
switch (subtype) {
9899
case SchemaKind.Object:
99-
return processObject(node, schema.items as IObjectNode, level + 1, [...path, 'items'], options);
100+
return processObject(node, schema.items as IObjectNode, level , [...path, 'items'], options);
100101
case SchemaKind.Array:
101-
return processArray(node, schema.items as IObjectNode, level + 1, [...path, 'items'], options);
102+
return processArray(node, schema.items as IObjectNode, level, [...path, 'items'], options);
102103
default:
103104
if (typeof subtype === 'string' && isCombiner(subtype)) {
104-
return processArray(node, schema.items as IObjectNode, level + 1, [...path, 'items'], options);
105+
return processArray(node, schema.items as IObjectNode, level , [...path, 'items'], options);
105106
}
106107
}
107108
}

src/tree/tree.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { isLocalRef } from '@stoplight/json';
2+
import { Tree, TreeListParentNode, TreeState } from '@stoplight/tree-list';
3+
import { JSONSchema4 } from 'json-schema';
4+
import { get } from 'lodash-es';
5+
import { SchemaNode } from '../types';
6+
import { isRefNode } from '../utils/guards';
7+
import { MetadataStore } from './metadata';
8+
import { populateTree } from './populateTree';
9+
10+
export class SchemaTree extends Tree {
11+
constructor(public schema: JSONSchema4, public state: TreeState, public defaultExpandedDepth: number) {
12+
super(Tree.createArtificialRoot());
13+
}
14+
15+
protected readonly visited = new WeakSet();
16+
17+
public populate() {
18+
const expanded = {};
19+
populateTree(this.schema, this.root, 0, [], {
20+
onNode: (node: SchemaNode, parentTreeNode, level: number): boolean => {
21+
if (isRefNode(node) && isLocalRef(node.$ref)) {
22+
expanded[node.id] = false;
23+
}
24+
25+
if (MetadataStore[parentTreeNode.id] && isRefNode(MetadataStore[parentTreeNode.id].schema)) return false;
26+
return level <= this.defaultExpandedDepth + 1;
27+
},
28+
});
29+
this.state.expanded = expanded;
30+
this.invalidate();
31+
}
32+
33+
public populateTreeFragment(parent: TreeListParentNode) {
34+
const { path } = MetadataStore[parent.id];
35+
const initialLevel = Tree.getLevel(parent);
36+
const artificialRoot = Tree.createArtificialRoot();
37+
populateTree(get(this.schema, path), artificialRoot, initialLevel, path, {
38+
onNode: (node: SchemaNode, parentTreeNode, level: number): boolean => {
39+
return level <= initialLevel + 1;
40+
},
41+
});
42+
this.insertTreeFragment(artificialRoot.children[0].children, parent.id);
43+
}
44+
45+
public unwrap(node: TreeListParentNode) {
46+
if (isRefNode(MetadataStore[node.id].schema)) {
47+
} else if (node.children.length === 0 && !this.visited.has(node)) {
48+
this.populateTreeFragment(node);
49+
super.unwrap(node);
50+
} else {
51+
super.unwrap(node);
52+
}
53+
}
54+
}

src/tree/walk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function* walk(schema: JSONSchema4[] | JSONSchema4): IterableIterator<Sch
7979
}
8080
} else {
8181
const node = processNode(schema);
82-
if (node !== void 0 ) {
82+
if (node !== void 0) {
8383
yield node;
8484
}
8585
}

yalc.lock

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
"packages": {
44
"@stoplight/tree-list": {
55
"signature": "2d0c8d7db40f2b21094b8c818d648978",
6-
"file": true,
7-
"replaced": "^4.8.0"
6+
"file": true
87
}
98
}
109
}

0 commit comments

Comments
 (0)