@jpmorganchase/elemental 3.1.1 → 4.0.1

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.
@@ -12,6 +12,7 @@ declare type SidebarLayoutProps = {
12
12
  tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
13
13
  tryItCorsProxy?: string;
14
14
  tryItOutDefaultServer?: string;
15
+ useCustomNav?: boolean;
15
16
  };
16
17
  export declare const APIWithSidebarLayout: React.FC<SidebarLayoutProps>;
17
18
  export {};
@@ -18,6 +18,7 @@ export interface CommonAPIProps extends RoutingProps {
18
18
  tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
19
19
  tryItCorsProxy?: string;
20
20
  tryItOutDefaultServer?: string;
21
+ useCustomNav?: boolean;
21
22
  }
22
23
  export declare const APIImpl: React.FC<APIProps>;
23
24
  export declare const API: React.FC<APIProps>;
@@ -1,4 +1,3 @@
1
- import { Story } from '@storybook/react';
2
1
  import * as React from 'react';
3
2
  import { APIProps } from './API';
4
3
  declare const _default: {
@@ -44,14 +43,14 @@ declare const _default: {
44
43
  };
45
44
  };
46
45
  export default _default;
47
- export declare const APIWithYamlProvidedDirectly: Story<APIProps>;
48
- export declare const APIWithJSONProvidedDirectly: Story<APIProps>;
49
- export declare const APIWithoutDescription: Story<APIProps>;
50
- export declare const APIWithInternalOperations: Story<APIProps>;
51
- export declare const OpenApi3Schema: Story<APIProps>;
52
- export declare const BadgesForSchema: Story<APIProps>;
53
- export declare const StackedLayout: Story<APIProps>;
54
- export declare const Box: Story<APIProps>;
55
- export declare const DigitalOcean: Story<APIProps>;
56
- export declare const Github: Story<APIProps>;
57
- export declare const Instagram: Story<APIProps>;
46
+ export declare const APIWithYamlProvidedDirectly: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
47
+ export declare const APIWithJSONProvidedDirectly: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
48
+ export declare const APIWithoutDescription: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
49
+ export declare const APIWithInternalOperations: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
50
+ export declare const OpenApi3Schema: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
51
+ export declare const BadgesForSchema: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
52
+ export declare const StackedLayout: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
53
+ export declare const Box: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
54
+ export declare const DigitalOcean: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
55
+ export declare const Github: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
56
+ export declare const Instagram: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
@@ -0,0 +1 @@
1
+ export declare const useGetOasNavTree: (apiDescriptionDocument: string | object) => any[];
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
package/index.d.ts CHANGED
@@ -1,2 +1,5 @@
1
1
  export type { APIProps } from './containers/API';
2
2
  export { API } from './containers/API';
3
+ export { useGetOasNavTree } from './hooks/oas-nav-tree/useGetOasNavTree';
4
+ export { useExportDocumentProps } from './hooks/useExportDocumentProps';
5
+ export { transformOasToServiceNode } from './utils/oas';
package/index.esm.js CHANGED
@@ -157,13 +157,20 @@ const isInternal = (node) => {
157
157
  return !!data['x-internal'];
158
158
  };
159
159
 
160
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
160
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, }) => {
161
161
  const container = React.useRef(null);
162
- const tree = React.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
162
+ const tree = React.useMemo(() => {
163
+ if (!useCustomNav)
164
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
165
+ else
166
+ return [];
167
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
163
168
  const location = useLocation();
164
169
  const { pathname } = location;
165
170
  const isRootPath = !pathname || pathname === '/';
166
171
  const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
172
+ React.useEffect(() => {
173
+ }, [pathname]);
167
174
  const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node]);
168
175
  if (!node) {
169
176
  const firstSlug = findFirstNodeSlug(tree);
@@ -186,12 +193,10 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
186
193
  React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
187
194
  React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
188
195
  React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
189
- return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
196
+ return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
190
197
  };
191
198
 
192
- const itemMatchesHash = (hash, item) => {
193
- return hash.substr(1) === `${item.name}-${item.data.method}`;
194
- };
199
+ const itemUriMatchesPathname = (itemUri, pathname) => itemUri === pathname;
195
200
  const TryItContext = React.createContext({
196
201
  hideTryIt: false,
197
202
  tryItCredentialsPolicy: 'omit',
@@ -208,24 +213,18 @@ const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps,
208
213
  };
209
214
  const Group = React.memo(({ group }) => {
210
215
  const [isExpanded, setIsExpanded] = React.useState(false);
211
- const { hash } = useLocation();
212
- const scrollRef = React.useRef(null);
213
- const urlHashMatches = hash.substr(1) === group.title;
216
+ const { pathname } = useLocation();
214
217
  const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
215
218
  const shouldExpand = React.useMemo(() => {
216
- return urlHashMatches || group.items.some(item => itemMatchesHash(hash, item));
217
- }, [group, hash, urlHashMatches]);
219
+ return group.items.some(item => itemUriMatchesPathname(item.uri, pathname));
220
+ }, [group, pathname]);
218
221
  React.useEffect(() => {
219
- var _a;
220
222
  if (shouldExpand) {
221
223
  setIsExpanded(true);
222
- if (urlHashMatches && ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop)) {
223
- window.scrollTo(0, scrollRef.current.offsetTop);
224
- }
225
224
  }
226
- }, [shouldExpand, urlHashMatches, group, hash]);
225
+ }, [shouldExpand]);
227
226
  return (React.createElement(Box, null,
228
- React.createElement(Flex, { ref: scrollRef, onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
227
+ React.createElement(Flex, { onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
229
228
  React.createElement(Box, { fontSize: "lg", fontWeight: "medium" }, group.title),
230
229
  React.createElement(Icon, { className: "sl-mr-2", icon: isExpanded ? 'chevron-down' : 'chevron-right', size: "sm" })),
231
230
  React.createElement(Collapse, { isOpen: isExpanded }, group.items.map(item => {
@@ -234,36 +233,42 @@ const Group = React.memo(({ group }) => {
234
233
  });
235
234
  const Item = React.memo(({ item }) => {
236
235
  const location = useLocation();
237
- const { hash } = location;
236
+ const { pathname } = location;
238
237
  const [isExpanded, setIsExpanded] = React.useState(false);
239
238
  const scrollRef = React.useRef(null);
240
239
  const color = HttpMethodColors[item.data.method] || 'gray';
241
240
  const isDeprecated = !!item.data.deprecated;
242
241
  const { hideTryIt, tryItCredentialsPolicy, corsProxy, tryItOutDefaultServer } = React.useContext(TryItContext);
243
- const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
242
+ const onClick = React.useCallback(() => {
243
+ setIsExpanded(!isExpanded);
244
+ if (window && window.location) {
245
+ window.history.pushState(null, '', `#${item.uri}`);
246
+ }
247
+ }, [isExpanded, item]);
244
248
  React.useEffect(() => {
245
- var _a;
246
- if (itemMatchesHash(hash, item)) {
249
+ if (itemUriMatchesPathname(item.uri, pathname)) {
247
250
  setIsExpanded(true);
248
- if ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop) {
249
- window.scrollTo(0, scrollRef.current.offsetTop);
251
+ if (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) {
252
+ scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current.scrollIntoView();
250
253
  }
251
254
  }
252
- }, [hash, item]);
255
+ }, [pathname, item]);
253
256
  return (React.createElement(Box, { ref: scrollRef, w: "full", my: 2, border: true, borderColor: { default: isExpanded ? 'light' : 'transparent', hover: 'light' }, bg: { default: isExpanded ? 'code' : 'transparent', hover: 'code' } },
254
257
  React.createElement(Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
255
258
  React.createElement(Box, { w: 24, textTransform: "uppercase", textAlign: "center", fontWeight: "semibold", border: true, rounded: true, px: 2, bg: "canvas", className: cn(`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`) }, item.data.method || 'UNKNOWN'),
256
- React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.name),
259
+ React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
257
260
  isDeprecated && React.createElement(DeprecatedBadge, null)),
258
- React.createElement(Collapse, { isOpen: isExpanded }, hideTryIt ? (React.createElement(Box, { as: ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React.createElement(Tabs, { appearance: "line" },
259
- React.createElement(TabList, null,
260
- React.createElement(Tab, null, "Docs"),
261
- React.createElement(Tab, null, "TryIt")),
262
- React.createElement(TabPanels, null,
263
- React.createElement(TabPanel, null,
264
- React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
265
- React.createElement(TabPanel, null,
266
- React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
261
+ React.createElement(Collapse, { isOpen: isExpanded },
262
+ React.createElement(Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
263
+ hideTryIt ? (React.createElement(Box, { as: ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React.createElement(Tabs, { appearance: "line" },
264
+ React.createElement(TabList, null,
265
+ React.createElement(Tab, null, "Docs"),
266
+ React.createElement(Tab, null, "TryIt")),
267
+ React.createElement(TabPanels, null,
268
+ React.createElement(TabPanel, null,
269
+ React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
270
+ React.createElement(TabPanel, null,
271
+ React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
267
272
  });
268
273
  const Collapse = ({ isOpen, children }) => {
269
274
  if (!isOpen)
@@ -483,7 +488,7 @@ const propsAreWithDocument = (props) => {
483
488
  return props.hasOwnProperty('apiDescriptionDocument');
484
489
  };
485
490
  const APIImpl = props => {
486
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
491
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
487
492
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
488
493
  const { data: fetchedDocument, error } = useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
489
494
  if (res.ok) {
@@ -510,8 +515,37 @@ const APIImpl = props => {
510
515
  return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
511
516
  React.createElement(NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
512
517
  }
513
- return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
518
+ return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav }))));
514
519
  };
515
520
  const API = flow(withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider)(APIImpl);
516
521
 
517
- export { API };
522
+ const useGetOasNavTree = (apiDescriptionDocument) => {
523
+ const parsedDocument = useParsedValue(apiDescriptionDocument);
524
+ const bundledDocument = useBundleRefsIntoDocument(parsedDocument);
525
+ if (!bundledDocument)
526
+ return [];
527
+ const groupSchemas = (tree) => {
528
+ const targetTitle = 'Schemas';
529
+ const newTree = tree.reduce((accumulator, currentObject) => {
530
+ var _a;
531
+ if (currentObject.title === targetTitle) {
532
+ accumulator.matchedObject = currentObject;
533
+ }
534
+ else if ((_a = currentObject.id) === null || _a === void 0 ? void 0 : _a.includes(targetTitle.toLowerCase())) {
535
+ accumulator.matchedObject.items = accumulator.matchedObject.items || [];
536
+ accumulator.matchedObject.items.push(currentObject);
537
+ }
538
+ else {
539
+ accumulator.others = accumulator.others || [];
540
+ accumulator.others.push(currentObject);
541
+ }
542
+ return accumulator;
543
+ }, {});
544
+ const navTree = [...newTree.others, newTree.matchedObject];
545
+ return navTree;
546
+ };
547
+ const apiTree = computeAPITree(transformOasToServiceNode(bundledDocument));
548
+ return groupSchemas(apiTree);
549
+ };
550
+
551
+ export { API, transformOasToServiceNode, useExportDocumentProps, useGetOasNavTree };
package/index.js CHANGED
@@ -190,13 +190,20 @@ const isInternal = (node) => {
190
190
  return !!data['x-internal'];
191
191
  };
192
192
 
193
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
193
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, }) => {
194
194
  const container = React__namespace.useRef(null);
195
- const tree = React__namespace.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
195
+ const tree = React__namespace.useMemo(() => {
196
+ if (!useCustomNav)
197
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
198
+ else
199
+ return [];
200
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
196
201
  const location = reactRouterDom.useLocation();
197
202
  const { pathname } = location;
198
203
  const isRootPath = !pathname || pathname === '/';
199
204
  const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
205
+ React__namespace.useEffect(() => {
206
+ }, [pathname]);
200
207
  const layoutOptions = React__namespace.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== types.NodeType.HttpService }), [hideTryIt, hideExport, node]);
201
208
  if (!node) {
202
209
  const firstSlug = findFirstNodeSlug(tree);
@@ -219,12 +226,10 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
219
226
  React__namespace.createElement(mosaic.Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
220
227
  React__namespace.createElement(elementalCore.TableOfContents, { tree: tree, activeId: pathname, Link: reactRouterDom.Link, onLinkClick: handleTocClick })),
221
228
  React__namespace.createElement(elementalCore.PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
222
- return (React__namespace.createElement(elementalCore.SidebarLayout, { ref: container, sidebar: sidebar }, node && (React__namespace.createElement(elementalCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
229
+ return (React__namespace.createElement(elementalCore.SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav }, node && (React__namespace.createElement(elementalCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
223
230
  };
224
231
 
225
- const itemMatchesHash = (hash, item) => {
226
- return hash.substr(1) === `${item.name}-${item.data.method}`;
227
- };
232
+ const itemUriMatchesPathname = (itemUri, pathname) => itemUri === pathname;
228
233
  const TryItContext = React__namespace.createContext({
229
234
  hideTryIt: false,
230
235
  tryItCredentialsPolicy: 'omit',
@@ -241,24 +246,18 @@ const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps,
241
246
  };
242
247
  const Group = React__namespace.memo(({ group }) => {
243
248
  const [isExpanded, setIsExpanded] = React__namespace.useState(false);
244
- const { hash } = reactRouterDom.useLocation();
245
- const scrollRef = React__namespace.useRef(null);
246
- const urlHashMatches = hash.substr(1) === group.title;
249
+ const { pathname } = reactRouterDom.useLocation();
247
250
  const onClick = React__namespace.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
248
251
  const shouldExpand = React__namespace.useMemo(() => {
249
- return urlHashMatches || group.items.some(item => itemMatchesHash(hash, item));
250
- }, [group, hash, urlHashMatches]);
252
+ return group.items.some(item => itemUriMatchesPathname(item.uri, pathname));
253
+ }, [group, pathname]);
251
254
  React__namespace.useEffect(() => {
252
- var _a;
253
255
  if (shouldExpand) {
254
256
  setIsExpanded(true);
255
- if (urlHashMatches && ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop)) {
256
- window.scrollTo(0, scrollRef.current.offsetTop);
257
- }
258
257
  }
259
- }, [shouldExpand, urlHashMatches, group, hash]);
258
+ }, [shouldExpand]);
260
259
  return (React__namespace.createElement(mosaic.Box, null,
261
- React__namespace.createElement(mosaic.Flex, { ref: scrollRef, onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
260
+ React__namespace.createElement(mosaic.Flex, { onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
262
261
  React__namespace.createElement(mosaic.Box, { fontSize: "lg", fontWeight: "medium" }, group.title),
263
262
  React__namespace.createElement(mosaic.Icon, { className: "sl-mr-2", icon: isExpanded ? 'chevron-down' : 'chevron-right', size: "sm" })),
264
263
  React__namespace.createElement(Collapse, { isOpen: isExpanded }, group.items.map(item => {
@@ -267,36 +266,42 @@ const Group = React__namespace.memo(({ group }) => {
267
266
  });
268
267
  const Item = React__namespace.memo(({ item }) => {
269
268
  const location = reactRouterDom.useLocation();
270
- const { hash } = location;
269
+ const { pathname } = location;
271
270
  const [isExpanded, setIsExpanded] = React__namespace.useState(false);
272
271
  const scrollRef = React__namespace.useRef(null);
273
272
  const color = elementalCore.HttpMethodColors[item.data.method] || 'gray';
274
273
  const isDeprecated = !!item.data.deprecated;
275
274
  const { hideTryIt, tryItCredentialsPolicy, corsProxy, tryItOutDefaultServer } = React__namespace.useContext(TryItContext);
276
- const onClick = React__namespace.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
275
+ const onClick = React__namespace.useCallback(() => {
276
+ setIsExpanded(!isExpanded);
277
+ if (window && window.location) {
278
+ window.history.pushState(null, '', `#${item.uri}`);
279
+ }
280
+ }, [isExpanded, item]);
277
281
  React__namespace.useEffect(() => {
278
- var _a;
279
- if (itemMatchesHash(hash, item)) {
282
+ if (itemUriMatchesPathname(item.uri, pathname)) {
280
283
  setIsExpanded(true);
281
- if ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop) {
282
- window.scrollTo(0, scrollRef.current.offsetTop);
284
+ if (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) {
285
+ scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current.scrollIntoView();
283
286
  }
284
287
  }
285
- }, [hash, item]);
288
+ }, [pathname, item]);
286
289
  return (React__namespace.createElement(mosaic.Box, { ref: scrollRef, w: "full", my: 2, border: true, borderColor: { default: isExpanded ? 'light' : 'transparent', hover: 'light' }, bg: { default: isExpanded ? 'code' : 'transparent', hover: 'code' } },
287
290
  React__namespace.createElement(mosaic.Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
288
291
  React__namespace.createElement(mosaic.Box, { w: 24, textTransform: "uppercase", textAlign: "center", fontWeight: "semibold", border: true, rounded: true, px: 2, bg: "canvas", className: cn__default["default"](`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`) }, item.data.method || 'UNKNOWN'),
289
- React__namespace.createElement(mosaic.Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.name),
292
+ React__namespace.createElement(mosaic.Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
290
293
  isDeprecated && React__namespace.createElement(elementalCore.DeprecatedBadge, null)),
291
- React__namespace.createElement(Collapse, { isOpen: isExpanded }, hideTryIt ? (React__namespace.createElement(mosaic.Box, { as: elementalCore.ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React__namespace.createElement(mosaic.Tabs, { appearance: "line" },
292
- React__namespace.createElement(mosaic.TabList, null,
293
- React__namespace.createElement(mosaic.Tab, null, "Docs"),
294
- React__namespace.createElement(mosaic.Tab, null, "TryIt")),
295
- React__namespace.createElement(mosaic.TabPanels, null,
296
- React__namespace.createElement(mosaic.TabPanel, null,
297
- React__namespace.createElement(elementalCore.ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
298
- React__namespace.createElement(mosaic.TabPanel, null,
299
- React__namespace.createElement(elementalCore.TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
294
+ React__namespace.createElement(Collapse, { isOpen: isExpanded },
295
+ React__namespace.createElement(mosaic.Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
296
+ hideTryIt ? (React__namespace.createElement(mosaic.Box, { as: elementalCore.ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React__namespace.createElement(mosaic.Tabs, { appearance: "line" },
297
+ React__namespace.createElement(mosaic.TabList, null,
298
+ React__namespace.createElement(mosaic.Tab, null, "Docs"),
299
+ React__namespace.createElement(mosaic.Tab, null, "TryIt")),
300
+ React__namespace.createElement(mosaic.TabPanels, null,
301
+ React__namespace.createElement(mosaic.TabPanel, null,
302
+ React__namespace.createElement(elementalCore.ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
303
+ React__namespace.createElement(mosaic.TabPanel, null,
304
+ React__namespace.createElement(elementalCore.TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
300
305
  });
301
306
  const Collapse = ({ isOpen, children }) => {
302
307
  if (!isOpen)
@@ -516,7 +521,7 @@ const propsAreWithDocument = (props) => {
516
521
  return props.hasOwnProperty('apiDescriptionDocument');
517
522
  };
518
523
  const APIImpl = props => {
519
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
524
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
520
525
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
521
526
  const { data: fetchedDocument, error } = reactQuery.useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
522
527
  if (res.ok) {
@@ -543,8 +548,40 @@ const APIImpl = props => {
543
548
  return (React__namespace.createElement(mosaic.Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
544
549
  React__namespace.createElement(elementalCore.NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
545
550
  }
546
- return (React__namespace.createElement(elementalCore.InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React__namespace.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React__namespace.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
551
+ return (React__namespace.createElement(elementalCore.InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React__namespace.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React__namespace.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav }))));
547
552
  };
548
553
  const API = flow__default["default"](elementalCore.withRouter, elementalCore.withStyles, elementalCore.withPersistenceBoundary, elementalCore.withMosaicProvider, elementalCore.withQueryClientProvider)(APIImpl);
549
554
 
555
+ const useGetOasNavTree = (apiDescriptionDocument) => {
556
+ const parsedDocument = elementalCore.useParsedValue(apiDescriptionDocument);
557
+ const bundledDocument = elementalCore.useBundleRefsIntoDocument(parsedDocument);
558
+ if (!bundledDocument)
559
+ return [];
560
+ const groupSchemas = (tree) => {
561
+ const targetTitle = 'Schemas';
562
+ const newTree = tree.reduce((accumulator, currentObject) => {
563
+ var _a;
564
+ if (currentObject.title === targetTitle) {
565
+ accumulator.matchedObject = currentObject;
566
+ }
567
+ else if ((_a = currentObject.id) === null || _a === void 0 ? void 0 : _a.includes(targetTitle.toLowerCase())) {
568
+ accumulator.matchedObject.items = accumulator.matchedObject.items || [];
569
+ accumulator.matchedObject.items.push(currentObject);
570
+ }
571
+ else {
572
+ accumulator.others = accumulator.others || [];
573
+ accumulator.others.push(currentObject);
574
+ }
575
+ return accumulator;
576
+ }, {});
577
+ const navTree = [...newTree.others, newTree.matchedObject];
578
+ return navTree;
579
+ };
580
+ const apiTree = computeAPITree(transformOasToServiceNode(bundledDocument));
581
+ return groupSchemas(apiTree);
582
+ };
583
+
550
584
  exports.API = API;
585
+ exports.transformOasToServiceNode = transformOasToServiceNode;
586
+ exports.useExportDocumentProps = useExportDocumentProps;
587
+ exports.useGetOasNavTree = useGetOasNavTree;
package/index.mjs CHANGED
@@ -157,13 +157,20 @@ const isInternal = (node) => {
157
157
  return !!data['x-internal'];
158
158
  };
159
159
 
160
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
160
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, }) => {
161
161
  const container = React.useRef(null);
162
- const tree = React.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
162
+ const tree = React.useMemo(() => {
163
+ if (!useCustomNav)
164
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
165
+ else
166
+ return [];
167
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
163
168
  const location = useLocation();
164
169
  const { pathname } = location;
165
170
  const isRootPath = !pathname || pathname === '/';
166
171
  const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
172
+ React.useEffect(() => {
173
+ }, [pathname]);
167
174
  const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node]);
168
175
  if (!node) {
169
176
  const firstSlug = findFirstNodeSlug(tree);
@@ -186,12 +193,10 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
186
193
  React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
187
194
  React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
188
195
  React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
189
- return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
196
+ return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
190
197
  };
191
198
 
192
- const itemMatchesHash = (hash, item) => {
193
- return hash.substr(1) === `${item.name}-${item.data.method}`;
194
- };
199
+ const itemUriMatchesPathname = (itemUri, pathname) => itemUri === pathname;
195
200
  const TryItContext = React.createContext({
196
201
  hideTryIt: false,
197
202
  tryItCredentialsPolicy: 'omit',
@@ -208,24 +213,18 @@ const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps,
208
213
  };
209
214
  const Group = React.memo(({ group }) => {
210
215
  const [isExpanded, setIsExpanded] = React.useState(false);
211
- const { hash } = useLocation();
212
- const scrollRef = React.useRef(null);
213
- const urlHashMatches = hash.substr(1) === group.title;
216
+ const { pathname } = useLocation();
214
217
  const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
215
218
  const shouldExpand = React.useMemo(() => {
216
- return urlHashMatches || group.items.some(item => itemMatchesHash(hash, item));
217
- }, [group, hash, urlHashMatches]);
219
+ return group.items.some(item => itemUriMatchesPathname(item.uri, pathname));
220
+ }, [group, pathname]);
218
221
  React.useEffect(() => {
219
- var _a;
220
222
  if (shouldExpand) {
221
223
  setIsExpanded(true);
222
- if (urlHashMatches && ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop)) {
223
- window.scrollTo(0, scrollRef.current.offsetTop);
224
- }
225
224
  }
226
- }, [shouldExpand, urlHashMatches, group, hash]);
225
+ }, [shouldExpand]);
227
226
  return (React.createElement(Box, null,
228
- React.createElement(Flex, { ref: scrollRef, onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
227
+ React.createElement(Flex, { onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
229
228
  React.createElement(Box, { fontSize: "lg", fontWeight: "medium" }, group.title),
230
229
  React.createElement(Icon, { className: "sl-mr-2", icon: isExpanded ? 'chevron-down' : 'chevron-right', size: "sm" })),
231
230
  React.createElement(Collapse, { isOpen: isExpanded }, group.items.map(item => {
@@ -234,36 +233,42 @@ const Group = React.memo(({ group }) => {
234
233
  });
235
234
  const Item = React.memo(({ item }) => {
236
235
  const location = useLocation();
237
- const { hash } = location;
236
+ const { pathname } = location;
238
237
  const [isExpanded, setIsExpanded] = React.useState(false);
239
238
  const scrollRef = React.useRef(null);
240
239
  const color = HttpMethodColors[item.data.method] || 'gray';
241
240
  const isDeprecated = !!item.data.deprecated;
242
241
  const { hideTryIt, tryItCredentialsPolicy, corsProxy, tryItOutDefaultServer } = React.useContext(TryItContext);
243
- const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
242
+ const onClick = React.useCallback(() => {
243
+ setIsExpanded(!isExpanded);
244
+ if (window && window.location) {
245
+ window.history.pushState(null, '', `#${item.uri}`);
246
+ }
247
+ }, [isExpanded, item]);
244
248
  React.useEffect(() => {
245
- var _a;
246
- if (itemMatchesHash(hash, item)) {
249
+ if (itemUriMatchesPathname(item.uri, pathname)) {
247
250
  setIsExpanded(true);
248
- if ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop) {
249
- window.scrollTo(0, scrollRef.current.offsetTop);
251
+ if (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) {
252
+ scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current.scrollIntoView();
250
253
  }
251
254
  }
252
- }, [hash, item]);
255
+ }, [pathname, item]);
253
256
  return (React.createElement(Box, { ref: scrollRef, w: "full", my: 2, border: true, borderColor: { default: isExpanded ? 'light' : 'transparent', hover: 'light' }, bg: { default: isExpanded ? 'code' : 'transparent', hover: 'code' } },
254
257
  React.createElement(Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
255
258
  React.createElement(Box, { w: 24, textTransform: "uppercase", textAlign: "center", fontWeight: "semibold", border: true, rounded: true, px: 2, bg: "canvas", className: cn(`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`) }, item.data.method || 'UNKNOWN'),
256
- React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.name),
259
+ React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
257
260
  isDeprecated && React.createElement(DeprecatedBadge, null)),
258
- React.createElement(Collapse, { isOpen: isExpanded }, hideTryIt ? (React.createElement(Box, { as: ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React.createElement(Tabs, { appearance: "line" },
259
- React.createElement(TabList, null,
260
- React.createElement(Tab, null, "Docs"),
261
- React.createElement(Tab, null, "TryIt")),
262
- React.createElement(TabPanels, null,
263
- React.createElement(TabPanel, null,
264
- React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
265
- React.createElement(TabPanel, null,
266
- React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
261
+ React.createElement(Collapse, { isOpen: isExpanded },
262
+ React.createElement(Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
263
+ hideTryIt ? (React.createElement(Box, { as: ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React.createElement(Tabs, { appearance: "line" },
264
+ React.createElement(TabList, null,
265
+ React.createElement(Tab, null, "Docs"),
266
+ React.createElement(Tab, null, "TryIt")),
267
+ React.createElement(TabPanels, null,
268
+ React.createElement(TabPanel, null,
269
+ React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
270
+ React.createElement(TabPanel, null,
271
+ React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
267
272
  });
268
273
  const Collapse = ({ isOpen, children }) => {
269
274
  if (!isOpen)
@@ -483,7 +488,7 @@ const propsAreWithDocument = (props) => {
483
488
  return props.hasOwnProperty('apiDescriptionDocument');
484
489
  };
485
490
  const APIImpl = props => {
486
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
491
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
487
492
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
488
493
  const { data: fetchedDocument, error } = useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
489
494
  if (res.ok) {
@@ -510,8 +515,37 @@ const APIImpl = props => {
510
515
  return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
511
516
  React.createElement(NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
512
517
  }
513
- return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
518
+ return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav }))));
514
519
  };
515
520
  const API = flow(withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider)(APIImpl);
516
521
 
517
- export { API };
522
+ const useGetOasNavTree = (apiDescriptionDocument) => {
523
+ const parsedDocument = useParsedValue(apiDescriptionDocument);
524
+ const bundledDocument = useBundleRefsIntoDocument(parsedDocument);
525
+ if (!bundledDocument)
526
+ return [];
527
+ const groupSchemas = (tree) => {
528
+ const targetTitle = 'Schemas';
529
+ const newTree = tree.reduce((accumulator, currentObject) => {
530
+ var _a;
531
+ if (currentObject.title === targetTitle) {
532
+ accumulator.matchedObject = currentObject;
533
+ }
534
+ else if ((_a = currentObject.id) === null || _a === void 0 ? void 0 : _a.includes(targetTitle.toLowerCase())) {
535
+ accumulator.matchedObject.items = accumulator.matchedObject.items || [];
536
+ accumulator.matchedObject.items.push(currentObject);
537
+ }
538
+ else {
539
+ accumulator.others = accumulator.others || [];
540
+ accumulator.others.push(currentObject);
541
+ }
542
+ return accumulator;
543
+ }, {});
544
+ const navTree = [...newTree.others, newTree.matchedObject];
545
+ return navTree;
546
+ };
547
+ const apiTree = computeAPITree(transformOasToServiceNode(bundledDocument));
548
+ return groupSchemas(apiTree);
549
+ };
550
+
551
+ export { API, transformOasToServiceNode, useExportDocumentProps, useGetOasNavTree };