@stoplight/elements-core 8.5.2 → 9.0.0

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.
@@ -0,0 +1,8 @@
1
+ interface ScrollToHashElementProps {
2
+ initialBehavior?: ScrollBehavior;
3
+ behavior?: ScrollBehavior;
4
+ inline?: ScrollLogicalPosition;
5
+ block?: ScrollLogicalPosition;
6
+ }
7
+ export declare const ScrollToHashElement: ({ behavior, initialBehavior, inline, block, }: ScrollToHashElementProps) => null;
8
+ export {};
@@ -1,3 +1,5 @@
1
1
  import * as React from 'react';
2
2
  import { RoutingProps } from '../types';
3
- export declare function withRouter<P extends RoutingProps>(WrappedComponent: React.ComponentType<P>): React.FC<P>;
3
+ export declare function withRouter<P extends RoutingProps>(WrappedComponent: React.ComponentType<P & {
4
+ outerRouter?: boolean;
5
+ }>): React.FC<P>;
package/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { Logo } from './components/Logo';
8
8
  export { DefaultSMDComponents } from './components/MarkdownViewer/CustomComponents/CodeComponent';
9
9
  export { CustomComponentMapping, MarkdownComponentsProvider, } from './components/MarkdownViewer/CustomComponents/Provider';
10
10
  export { ReactRouterMarkdownLink } from './components/MarkdownViewer/CustomComponents/ReactRouterLink';
11
+ export { ScrollToHashElement } from './components/MarkdownViewer/CustomComponents/ScrollToHashElement';
11
12
  export { NonIdealState } from './components/NonIdealState';
12
13
  export { PoweredByLink } from './components/PoweredByLink';
13
14
  export { TableOfContents } from './components/TableOfContents';
@@ -33,5 +34,6 @@ export { Divider, Group, ITableOfContentsTree, Item, ParsedNode, RoutingProps, T
33
34
  export { isHttpOperation, isHttpService, isHttpWebhookOperation } from './utils/guards';
34
35
  export { ReferenceResolver } from './utils/ref-resolving/ReferenceResolver';
35
36
  export { createResolvedObject } from './utils/ref-resolving/resolvedObject';
36
- export { slugify } from './utils/string';
37
+ export { slugify, resolveRelativeLink } from './utils/string';
37
38
  export { createElementClass } from './web-components/createElementClass';
39
+ export { resolveUrl } from './utils/http-spec/IServer';
package/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { __rest, __awaiter } from 'tslib';
2
2
  import * as React from 'react';
3
- import React__default, { useContext, memo, useMemo, useState, useEffect } from 'react';
3
+ import React__default, { useContext, memo, useMemo, useState, useEffect, useRef, useLayoutEffect } from 'react';
4
4
  import { convertToJsonSchema } from '@stoplight/http-spec/oas';
5
5
  import { resolveInlineRef, hasRef, isPlainObject as isPlainObject$1, safeParse, safeStringify } from '@stoplight/json';
6
6
  import isArray from 'lodash/isArray.js';
@@ -10,7 +10,7 @@ import { parse } from '@stoplight/yaml';
10
10
  import { isArray as isArray$1, Box, useBreakpoints, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, Callout, useModalState, Modal, ListBox, ListBoxItem, TabList, Tab, Tabs, TabPanels, TabPanel, Heading, useClipboard, Drawer, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
11
11
  import isObject from 'lodash/isObject.js';
12
12
  import { withErrorBoundary } from '@stoplight/react-error-boundary';
13
- import { useLocation, Link as Link$1, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
13
+ import { useLocation, Link as Link$1, BrowserRouter, MemoryRouter, HashRouter, useInRouterContext, Routes, Route } from 'react-router-dom';
14
14
  import { MarkdownViewer as MarkdownViewer$1, DefaultSMDComponents, MarkdownViewerProvider } from '@stoplight/markdown-viewer';
15
15
  export { DefaultSMDComponents } from '@stoplight/markdown-viewer';
16
16
  import cn from 'classnames';
@@ -51,8 +51,8 @@ import keys from 'lodash/keys.js';
51
51
  import sortBy from 'lodash/sortBy.js';
52
52
  import isNil from 'lodash/isNil.js';
53
53
  import omitBy from 'lodash/omitBy.js';
54
- import { HashLink } from 'react-router-hash-link';
55
54
  import { QueryClient, useQueryClient, QueryClientProvider } from 'react-query';
55
+ import { StaticRouter } from 'react-router-dom/server.js';
56
56
  import $RefParser from '@stoplight/json-schema-ref-parser';
57
57
  import * as PropTypes from 'prop-types';
58
58
  import isEqual from 'lodash/isEqual.js';
@@ -1105,6 +1105,9 @@ function slugify(name) {
1105
1105
  .replace(/^-/, '')
1106
1106
  .replace(/-$/, '');
1107
1107
  }
1108
+ const resolveRelativeLink = (slug) => {
1109
+ return slug ? slug.replace(/^\//, '') : '.';
1110
+ };
1108
1111
 
1109
1112
  const isApiKeySecurityScheme = (maybeIApiKey) => isObject(maybeIApiKey) && maybeIApiKey.type === 'apiKey';
1110
1113
  const isOAuth2SecurityScheme = (maybeIOAuth2) => isObject(maybeIOAuth2) && maybeIOAuth2.type === 'oauth2';
@@ -3501,7 +3504,6 @@ const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefaul
3501
3504
  const container = React.useRef(null);
3502
3505
  const child = React.useRef(null);
3503
3506
  const firstRender = useFirstRender();
3504
- const makeSlugAbsoluteRoute = useRouterType() == 'hash';
3505
3507
  React.useEffect(() => {
3506
3508
  setTimeout(() => {
3507
3509
  const scrollPosition = firstRender ? 'center' : 'nearest';
@@ -3522,7 +3524,7 @@ const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefaul
3522
3524
  if (isDivider(item)) {
3523
3525
  return React.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3524
3526
  }
3525
- return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3527
+ return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3526
3528
  }))))));
3527
3529
  });
3528
3530
  TableOfContents.displayName = 'TableOfContents';
@@ -3530,23 +3532,23 @@ const Divider = React.memo(({ item, isInResponsiveMode = false }) => {
3530
3532
  return (React.createElement(Box, { pl: 4, mb: 2, mt: 6, textTransform: "uppercase", fontSize: isInResponsiveMode ? 'lg' : 'sm', lineHeight: "relaxed", letterSpacing: "wide", fontWeight: "bold" }, item.title));
3531
3533
  });
3532
3534
  Divider.displayName = 'Divider';
3533
- const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, makeSlugAbsoluteRoute, onLinkClick }) => {
3535
+ const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick }) => {
3534
3536
  if (isExternalLink(item)) {
3535
3537
  return (React.createElement(Box, { as: "a", href: item.url, target: "_blank", rel: "noopener noreferrer", display: "block" },
3536
3538
  React.createElement(Item, { isInResponsiveMode: isInResponsiveMode, depth: depth, title: item.title, meta: React.createElement(Box, { as: Icon, icon: ['fas', 'external-link'] }) })));
3537
3539
  }
3538
3540
  else if (isGroup(item) || isNodeGroup(item)) {
3539
- return (React.createElement(Group, { depth: depth, item: item, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3541
+ return (React.createElement(Group, { depth: depth, item: item, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3540
3542
  }
3541
3543
  else if (isNode(item)) {
3542
- return (React.createElement(Node, { depth: depth, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute, item: item, onLinkClick: onLinkClick, meta: item.meta ? (React.createElement(Box, { color: NODE_META_COLOR[item.meta], textTransform: "uppercase", fontWeight: "medium" }, item.meta)) : (NODE_TYPE_META_ICON[item.type] && (React.createElement(Flex, { alignItems: "center" },
3544
+ return (React.createElement(Node, { depth: depth, isInResponsiveMode: isInResponsiveMode, item: item, onLinkClick: onLinkClick, meta: item.meta ? (React.createElement(Box, { color: NODE_META_COLOR[item.meta], textTransform: "uppercase", fontWeight: "medium" }, item.meta)) : (NODE_TYPE_META_ICON[item.type] && (React.createElement(Flex, { alignItems: "center" },
3543
3545
  item.version && React.createElement(Version, { value: item.version }),
3544
3546
  item.type !== 'model' && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_META_ICON[item.type] }))))) }));
3545
3547
  }
3546
3548
  return null;
3547
3549
  });
3548
3550
  GroupItem.displayName = 'GroupItem';
3549
- const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, makeSlugAbsoluteRoute, onLinkClick = () => { } }) => {
3551
+ const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick = () => { } }) => {
3550
3552
  const activeId = React.useContext(ActiveIdContext);
3551
3553
  const [isOpen, setIsOpen] = React.useState(() => isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault));
3552
3554
  const hasActive = !!activeId && hasActiveItem(item.items, activeId);
@@ -3574,7 +3576,7 @@ const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMo
3574
3576
  const showAsActive = hasActive && !isOpen;
3575
3577
  let elem;
3576
3578
  if (isNodeGroup(item)) {
3577
- elem = (React.createElement(Node, { depth: depth, item: item, meta: meta, showAsActive: showAsActive, onClick: handleClick, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3579
+ elem = (React.createElement(Node, { depth: depth, item: item, meta: meta, showAsActive: showAsActive, onClick: handleClick, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3578
3580
  }
3579
3581
  else {
3580
3582
  elem = (React.createElement(Item, { isInResponsiveMode: isInResponsiveMode, title: item.title, meta: meta, onClick: handleClick, depth: depth, isActive: showAsActive, icon: item.itemsType &&
@@ -3584,7 +3586,7 @@ const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMo
3584
3586
  elem,
3585
3587
  isOpen &&
3586
3588
  item.items.map((groupItem, key) => {
3587
- return (React.createElement(GroupItem, { key: key, item: groupItem, depth: depth + 1, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3589
+ return (React.createElement(GroupItem, { key: key, item: groupItem, depth: depth + 1, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3588
3590
  })));
3589
3591
  });
3590
3592
  Group.displayName = 'Group';
@@ -3598,7 +3600,7 @@ const Item = React.memo(({ depth, isActive, id, title, meta, icon, isInResponsiv
3598
3600
  React.createElement(Flex, { alignItems: "center", fontSize: isInResponsiveMode ? 'base' : 'xs' }, meta)));
3599
3601
  });
3600
3602
  Item.displayName = 'Item';
3601
- const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, makeSlugAbsoluteRoute, onClick, onLinkClick = () => { } }) => {
3603
+ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, onClick, onLinkClick = () => { } }) => {
3602
3604
  const activeId = React.useContext(ActiveIdContext);
3603
3605
  const isActive = activeId === item.slug || activeId === item.id;
3604
3606
  const LinkComponent = React.useContext(LinkContext);
@@ -3614,7 +3616,7 @@ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode,
3614
3616
  onClick(e, isActive ? undefined : true);
3615
3617
  }
3616
3618
  };
3617
- return (React.createElement(Box, { as: LinkComponent, to: makeSlugAbsoluteRoute && !item.slug.startsWith('/') ? `/${item.slug}` : item.slug, display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3619
+ return (React.createElement(Box, { as: LinkComponent, to: resolveRelativeLink(item.slug), display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3618
3620
  React.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: handleClick })));
3619
3621
  });
3620
3622
  Node.displayName = 'Node';
@@ -3879,7 +3881,66 @@ const ReactRouterMarkdownLink = ({ title, to, href: _href, children, }) => {
3879
3881
  if (isExternal) {
3880
3882
  return (React__default.createElement("a", { target: "_blank", rel: "noreferrer noopener", href: href, title: title }, children));
3881
3883
  }
3882
- return (React__default.createElement(HashLink, { to: href, title: title }, children));
3884
+ return (React__default.createElement("a", { href: href, title: title }, children));
3885
+ };
3886
+
3887
+ const ScrollToHashElement = ({ behavior = 'auto', initialBehavior = 'auto', inline = 'nearest', block = 'start', }) => {
3888
+ const [hash, setHash] = useState(window.location.hash);
3889
+ const [count, setCount] = useState(0);
3890
+ const originalListeners = useRef({});
3891
+ const [firstRun, setFirstRun] = useState(true);
3892
+ useEffect(() => setFirstRun(false), []);
3893
+ useEffect(() => {
3894
+ const handleLocationChange = () => {
3895
+ setHash(window.location.hash);
3896
+ setCount((count) => count + 1);
3897
+ };
3898
+ const onPopState = () => {
3899
+ window.dispatchEvent(new Event('locationchange'));
3900
+ };
3901
+ const addWindowListeners = () => {
3902
+ originalListeners.current.pushState = window.history.pushState;
3903
+ originalListeners.current.replaceState = window.history.replaceState;
3904
+ window.history.pushState = function (...args) {
3905
+ const result = originalListeners.current.pushState.apply(this, args);
3906
+ window.dispatchEvent(new Event('pushstate'));
3907
+ window.dispatchEvent(new Event('locationchange'));
3908
+ return result;
3909
+ };
3910
+ window.history.replaceState = function (...args) {
3911
+ const result = originalListeners.current.replaceState.apply(this, args);
3912
+ window.dispatchEvent(new Event('replacestate'));
3913
+ window.dispatchEvent(new Event('locationchange'));
3914
+ return result;
3915
+ };
3916
+ window.addEventListener('popstate', onPopState);
3917
+ window.addEventListener('locationchange', handleLocationChange);
3918
+ };
3919
+ const removeWindowListeners = () => {
3920
+ window.history.pushState = originalListeners.current.pushState;
3921
+ window.history.replaceState = originalListeners.current.replaceState;
3922
+ window.removeEventListener('popstate', onPopState);
3923
+ window.removeEventListener('locationchange', handleLocationChange);
3924
+ };
3925
+ addWindowListeners();
3926
+ return removeWindowListeners;
3927
+ }, []);
3928
+ useLayoutEffect(() => {
3929
+ const removeHashCharacter = (str) => {
3930
+ return str.slice(1);
3931
+ };
3932
+ if (hash) {
3933
+ const element = document.getElementById(removeHashCharacter(hash));
3934
+ if (element) {
3935
+ element.scrollIntoView({
3936
+ behavior: firstRun ? initialBehavior : behavior,
3937
+ inline: inline,
3938
+ block: block,
3939
+ });
3940
+ }
3941
+ }
3942
+ }, [hash, count, firstRun]);
3943
+ return null;
3883
3944
  };
3884
3945
 
3885
3946
  const NonIdealState = ({ description, icon, title }) => {
@@ -3959,18 +4020,29 @@ const components = {
3959
4020
  return React.createElement(LinkHeading, Object.assign({ size: 4 }, props));
3960
4021
  },
3961
4022
  };
4023
+ const InternalRoutes = ({ children }) => {
4024
+ return (React.createElement(Routes, null,
4025
+ React.createElement(Route, { path: "/*", element: React.createElement(MarkdownComponentsProvider, { value: components },
4026
+ React.createElement(ScrollToHashElement, null),
4027
+ children) })));
4028
+ };
3962
4029
  function withRouter(WrappedComponent) {
3963
4030
  const WithRouter = (props) => {
3964
4031
  var _a, _b, _c;
4032
+ const outerRouter = useInRouterContext();
3965
4033
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
3966
4034
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
3967
4035
  const routerType = (_c = props.router) !== null && _c !== void 0 ? _c : 'history';
3968
4036
  const { Router, routerProps } = useRouter(routerType, basePath, staticRouterPath);
4037
+ if (!outerRouter) {
4038
+ return (React.createElement(RouterTypeContext.Provider, { value: routerType },
4039
+ React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
4040
+ React.createElement(InternalRoutes, null,
4041
+ React.createElement(WrappedComponent, Object.assign({}, props, { outerRouter: false }))))));
4042
+ }
3969
4043
  return (React.createElement(RouterTypeContext.Provider, { value: routerType },
3970
- React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
3971
- React.createElement(Route, { path: "/" },
3972
- React.createElement(MarkdownComponentsProvider, { value: components },
3973
- React.createElement(WrappedComponent, Object.assign({}, props)))))));
4044
+ React.createElement(InternalRoutes, null,
4045
+ React.createElement(WrappedComponent, Object.assign({}, props, { outerRouter: true })))));
3974
4046
  };
3975
4047
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
3976
4048
  return WithRouter;
@@ -4140,4 +4212,4 @@ const createElementClass = (Component, propDescriptors) => {
4140
4212
  };
4141
4213
  };
4142
4214
 
4143
- export { DeprecatedBadge, Docs, ElementsOptionsProvider, ExportButton, HttpMethodColors, InlineRefResolverProvider, LinkHeading, Logo, MarkdownComponentsProvider, MockingProvider, NodeTypeColors, NodeTypeIconDefs, NodeTypePrettyName, NonIdealState, ParsedDocs, PersistenceContextProvider, PoweredByLink, ReactRouterMarkdownLink, ResponsiveSidebarLayout, RouterTypeContext, SidebarLayout, Styled, TableOfContents, TryIt, TryItWithRequestSamples, createElementClass, createResolvedObject, findFirstNode, isHttpOperation, isHttpService, isHttpWebhookOperation, slugify, useBundleRefsIntoDocument, useParsedData, useParsedValue, useResponsiveLayout, useRouter, withMosaicProvider, withPersistenceBoundary, withQueryClientProvider, withRouter, withStyles };
4215
+ export { DeprecatedBadge, Docs, ElementsOptionsProvider, ExportButton, HttpMethodColors, InlineRefResolverProvider, LinkHeading, Logo, MarkdownComponentsProvider, MockingProvider, NodeTypeColors, NodeTypeIconDefs, NodeTypePrettyName, NonIdealState, ParsedDocs, PersistenceContextProvider, PoweredByLink, ReactRouterMarkdownLink, ResponsiveSidebarLayout, RouterTypeContext, ScrollToHashElement, SidebarLayout, Styled, TableOfContents, TryIt, TryItWithRequestSamples, createElementClass, createResolvedObject, findFirstNode, isHttpOperation, isHttpService, isHttpWebhookOperation, resolveRelativeLink, resolveUrl, slugify, useBundleRefsIntoDocument, useParsedData, useParsedValue, useResponsiveLayout, useRouter, withMosaicProvider, withPersistenceBoundary, withQueryClientProvider, withRouter, withStyles };
package/index.js CHANGED
@@ -51,8 +51,8 @@ var keys = require('lodash/keys.js');
51
51
  var sortBy = require('lodash/sortBy.js');
52
52
  var isNil = require('lodash/isNil.js');
53
53
  var omitBy = require('lodash/omitBy.js');
54
- var reactRouterHashLink = require('react-router-hash-link');
55
54
  var reactQuery = require('react-query');
55
+ var server_js = require('react-router-dom/server.js');
56
56
  var $RefParser = require('@stoplight/json-schema-ref-parser');
57
57
  var PropTypes = require('prop-types');
58
58
  var isEqual = require('lodash/isEqual.js');
@@ -1127,6 +1127,9 @@ function slugify(name) {
1127
1127
  .replace(/^-/, '')
1128
1128
  .replace(/-$/, '');
1129
1129
  }
1130
+ const resolveRelativeLink = (slug) => {
1131
+ return slug ? slug.replace(/^\//, '') : '.';
1132
+ };
1130
1133
 
1131
1134
  const isApiKeySecurityScheme = (maybeIApiKey) => isObject(maybeIApiKey) && maybeIApiKey.type === 'apiKey';
1132
1135
  const isOAuth2SecurityScheme = (maybeIOAuth2) => isObject(maybeIOAuth2) && maybeIOAuth2.type === 'oauth2';
@@ -3523,7 +3526,6 @@ const TableOfContents = React__namespace.memo(({ tree, activeId, Link, maxDepthO
3523
3526
  const container = React__namespace.useRef(null);
3524
3527
  const child = React__namespace.useRef(null);
3525
3528
  const firstRender = useFirstRender();
3526
- const makeSlugAbsoluteRoute = useRouterType() == 'hash';
3527
3529
  React__namespace.useEffect(() => {
3528
3530
  setTimeout(() => {
3529
3531
  const scrollPosition = firstRender ? 'center' : 'nearest';
@@ -3544,7 +3546,7 @@ const TableOfContents = React__namespace.memo(({ tree, activeId, Link, maxDepthO
3544
3546
  if (isDivider(item)) {
3545
3547
  return React__namespace.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3546
3548
  }
3547
- return (React__namespace.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3549
+ return (React__namespace.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3548
3550
  }))))));
3549
3551
  });
3550
3552
  TableOfContents.displayName = 'TableOfContents';
@@ -3552,23 +3554,23 @@ const Divider = React__namespace.memo(({ item, isInResponsiveMode = false }) =>
3552
3554
  return (React__namespace.createElement(mosaic.Box, { pl: 4, mb: 2, mt: 6, textTransform: "uppercase", fontSize: isInResponsiveMode ? 'lg' : 'sm', lineHeight: "relaxed", letterSpacing: "wide", fontWeight: "bold" }, item.title));
3553
3555
  });
3554
3556
  Divider.displayName = 'Divider';
3555
- const GroupItem = React__namespace.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, makeSlugAbsoluteRoute, onLinkClick }) => {
3557
+ const GroupItem = React__namespace.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick }) => {
3556
3558
  if (isExternalLink(item)) {
3557
3559
  return (React__namespace.createElement(mosaic.Box, { as: "a", href: item.url, target: "_blank", rel: "noopener noreferrer", display: "block" },
3558
3560
  React__namespace.createElement(Item, { isInResponsiveMode: isInResponsiveMode, depth: depth, title: item.title, meta: React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, icon: ['fas', 'external-link'] }) })));
3559
3561
  }
3560
3562
  else if (isGroup(item) || isNodeGroup(item)) {
3561
- return (React__namespace.createElement(Group, { depth: depth, item: item, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3563
+ return (React__namespace.createElement(Group, { depth: depth, item: item, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3562
3564
  }
3563
3565
  else if (isNode(item)) {
3564
- return (React__namespace.createElement(Node, { depth: depth, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute, item: item, onLinkClick: onLinkClick, meta: item.meta ? (React__namespace.createElement(mosaic.Box, { color: NODE_META_COLOR[item.meta], textTransform: "uppercase", fontWeight: "medium" }, item.meta)) : (NODE_TYPE_META_ICON[item.type] && (React__namespace.createElement(mosaic.Flex, { alignItems: "center" },
3566
+ return (React__namespace.createElement(Node, { depth: depth, isInResponsiveMode: isInResponsiveMode, item: item, onLinkClick: onLinkClick, meta: item.meta ? (React__namespace.createElement(mosaic.Box, { color: NODE_META_COLOR[item.meta], textTransform: "uppercase", fontWeight: "medium" }, item.meta)) : (NODE_TYPE_META_ICON[item.type] && (React__namespace.createElement(mosaic.Flex, { alignItems: "center" },
3565
3567
  item.version && React__namespace.createElement(Version, { value: item.version }),
3566
3568
  item.type !== 'model' && (React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_META_ICON[item.type] }))))) }));
3567
3569
  }
3568
3570
  return null;
3569
3571
  });
3570
3572
  GroupItem.displayName = 'GroupItem';
3571
- const Group = React__namespace.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, makeSlugAbsoluteRoute, onLinkClick = () => { } }) => {
3573
+ const Group = React__namespace.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick = () => { } }) => {
3572
3574
  const activeId = React__namespace.useContext(ActiveIdContext);
3573
3575
  const [isOpen, setIsOpen] = React__namespace.useState(() => isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault));
3574
3576
  const hasActive = !!activeId && hasActiveItem(item.items, activeId);
@@ -3596,7 +3598,7 @@ const Group = React__namespace.memo(({ depth, item, maxDepthOpenByDefault, isInR
3596
3598
  const showAsActive = hasActive && !isOpen;
3597
3599
  let elem;
3598
3600
  if (isNodeGroup(item)) {
3599
- elem = (React__namespace.createElement(Node, { depth: depth, item: item, meta: meta, showAsActive: showAsActive, onClick: handleClick, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3601
+ elem = (React__namespace.createElement(Node, { depth: depth, item: item, meta: meta, showAsActive: showAsActive, onClick: handleClick, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3600
3602
  }
3601
3603
  else {
3602
3604
  elem = (React__namespace.createElement(Item, { isInResponsiveMode: isInResponsiveMode, title: item.title, meta: meta, onClick: handleClick, depth: depth, isActive: showAsActive, icon: item.itemsType &&
@@ -3606,7 +3608,7 @@ const Group = React__namespace.memo(({ depth, item, maxDepthOpenByDefault, isInR
3606
3608
  elem,
3607
3609
  isOpen &&
3608
3610
  item.items.map((groupItem, key) => {
3609
- return (React__namespace.createElement(GroupItem, { key: key, item: groupItem, depth: depth + 1, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3611
+ return (React__namespace.createElement(GroupItem, { key: key, item: groupItem, depth: depth + 1, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3610
3612
  })));
3611
3613
  });
3612
3614
  Group.displayName = 'Group';
@@ -3620,7 +3622,7 @@ const Item = React__namespace.memo(({ depth, isActive, id, title, meta, icon, is
3620
3622
  React__namespace.createElement(mosaic.Flex, { alignItems: "center", fontSize: isInResponsiveMode ? 'base' : 'xs' }, meta)));
3621
3623
  });
3622
3624
  Item.displayName = 'Item';
3623
- const Node = React__namespace.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, makeSlugAbsoluteRoute, onClick, onLinkClick = () => { } }) => {
3625
+ const Node = React__namespace.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, onClick, onLinkClick = () => { } }) => {
3624
3626
  const activeId = React__namespace.useContext(ActiveIdContext);
3625
3627
  const isActive = activeId === item.slug || activeId === item.id;
3626
3628
  const LinkComponent = React__namespace.useContext(LinkContext);
@@ -3636,7 +3638,7 @@ const Node = React__namespace.memo(({ item, depth, meta, showAsActive, isInRespo
3636
3638
  onClick(e, isActive ? undefined : true);
3637
3639
  }
3638
3640
  };
3639
- return (React__namespace.createElement(mosaic.Box, { as: LinkComponent, to: makeSlugAbsoluteRoute && !item.slug.startsWith('/') ? `/${item.slug}` : item.slug, display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3641
+ return (React__namespace.createElement(mosaic.Box, { as: LinkComponent, to: resolveRelativeLink(item.slug), display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3640
3642
  React__namespace.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: handleClick })));
3641
3643
  });
3642
3644
  Node.displayName = 'Node';
@@ -3901,7 +3903,66 @@ const ReactRouterMarkdownLink = ({ title, to, href: _href, children, }) => {
3901
3903
  if (isExternal) {
3902
3904
  return (React.createElement("a", { target: "_blank", rel: "noreferrer noopener", href: href, title: title }, children));
3903
3905
  }
3904
- return (React.createElement(reactRouterHashLink.HashLink, { to: href, title: title }, children));
3906
+ return (React.createElement("a", { href: href, title: title }, children));
3907
+ };
3908
+
3909
+ const ScrollToHashElement = ({ behavior = 'auto', initialBehavior = 'auto', inline = 'nearest', block = 'start', }) => {
3910
+ const [hash, setHash] = React.useState(window.location.hash);
3911
+ const [count, setCount] = React.useState(0);
3912
+ const originalListeners = React.useRef({});
3913
+ const [firstRun, setFirstRun] = React.useState(true);
3914
+ React.useEffect(() => setFirstRun(false), []);
3915
+ React.useEffect(() => {
3916
+ const handleLocationChange = () => {
3917
+ setHash(window.location.hash);
3918
+ setCount((count) => count + 1);
3919
+ };
3920
+ const onPopState = () => {
3921
+ window.dispatchEvent(new Event('locationchange'));
3922
+ };
3923
+ const addWindowListeners = () => {
3924
+ originalListeners.current.pushState = window.history.pushState;
3925
+ originalListeners.current.replaceState = window.history.replaceState;
3926
+ window.history.pushState = function (...args) {
3927
+ const result = originalListeners.current.pushState.apply(this, args);
3928
+ window.dispatchEvent(new Event('pushstate'));
3929
+ window.dispatchEvent(new Event('locationchange'));
3930
+ return result;
3931
+ };
3932
+ window.history.replaceState = function (...args) {
3933
+ const result = originalListeners.current.replaceState.apply(this, args);
3934
+ window.dispatchEvent(new Event('replacestate'));
3935
+ window.dispatchEvent(new Event('locationchange'));
3936
+ return result;
3937
+ };
3938
+ window.addEventListener('popstate', onPopState);
3939
+ window.addEventListener('locationchange', handleLocationChange);
3940
+ };
3941
+ const removeWindowListeners = () => {
3942
+ window.history.pushState = originalListeners.current.pushState;
3943
+ window.history.replaceState = originalListeners.current.replaceState;
3944
+ window.removeEventListener('popstate', onPopState);
3945
+ window.removeEventListener('locationchange', handleLocationChange);
3946
+ };
3947
+ addWindowListeners();
3948
+ return removeWindowListeners;
3949
+ }, []);
3950
+ React.useLayoutEffect(() => {
3951
+ const removeHashCharacter = (str) => {
3952
+ return str.slice(1);
3953
+ };
3954
+ if (hash) {
3955
+ const element = document.getElementById(removeHashCharacter(hash));
3956
+ if (element) {
3957
+ element.scrollIntoView({
3958
+ behavior: firstRun ? initialBehavior : behavior,
3959
+ inline: inline,
3960
+ block: block,
3961
+ });
3962
+ }
3963
+ }
3964
+ }, [hash, count, firstRun]);
3965
+ return null;
3905
3966
  };
3906
3967
 
3907
3968
  const NonIdealState = ({ description, icon, title }) => {
@@ -3955,7 +4016,7 @@ const RouterComponent = {
3955
4016
  history: reactRouterDom.BrowserRouter,
3956
4017
  memory: reactRouterDom.MemoryRouter,
3957
4018
  hash: reactRouterDom.HashRouter,
3958
- static: reactRouterDom.StaticRouter,
4019
+ static: server_js.StaticRouter,
3959
4020
  };
3960
4021
  const useRouter = (router, basePath, staticRouterPath) => {
3961
4022
  const Router = RouterComponent[router];
@@ -3981,18 +4042,29 @@ const components = {
3981
4042
  return React__namespace.createElement(LinkHeading, Object.assign({ size: 4 }, props));
3982
4043
  },
3983
4044
  };
4045
+ const InternalRoutes = ({ children }) => {
4046
+ return (React__namespace.createElement(reactRouterDom.Routes, null,
4047
+ React__namespace.createElement(reactRouterDom.Route, { path: "/*", element: React__namespace.createElement(MarkdownComponentsProvider, { value: components },
4048
+ React__namespace.createElement(ScrollToHashElement, null),
4049
+ children) })));
4050
+ };
3984
4051
  function withRouter(WrappedComponent) {
3985
4052
  const WithRouter = (props) => {
3986
4053
  var _a, _b, _c;
4054
+ const outerRouter = reactRouterDom.useInRouterContext();
3987
4055
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
3988
4056
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
3989
4057
  const routerType = (_c = props.router) !== null && _c !== void 0 ? _c : 'history';
3990
4058
  const { Router, routerProps } = useRouter(routerType, basePath, staticRouterPath);
4059
+ if (!outerRouter) {
4060
+ return (React__namespace.createElement(RouterTypeContext.Provider, { value: routerType },
4061
+ React__namespace.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
4062
+ React__namespace.createElement(InternalRoutes, null,
4063
+ React__namespace.createElement(WrappedComponent, Object.assign({}, props, { outerRouter: false }))))));
4064
+ }
3991
4065
  return (React__namespace.createElement(RouterTypeContext.Provider, { value: routerType },
3992
- React__namespace.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
3993
- React__namespace.createElement(reactRouterDom.Route, { path: "/" },
3994
- React__namespace.createElement(MarkdownComponentsProvider, { value: components },
3995
- React__namespace.createElement(WrappedComponent, Object.assign({}, props)))))));
4066
+ React__namespace.createElement(InternalRoutes, null,
4067
+ React__namespace.createElement(WrappedComponent, Object.assign({}, props, { outerRouter: true })))));
3996
4068
  };
3997
4069
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
3998
4070
  return WithRouter;
@@ -4186,6 +4258,7 @@ exports.PoweredByLink = PoweredByLink;
4186
4258
  exports.ReactRouterMarkdownLink = ReactRouterMarkdownLink;
4187
4259
  exports.ResponsiveSidebarLayout = ResponsiveSidebarLayout;
4188
4260
  exports.RouterTypeContext = RouterTypeContext;
4261
+ exports.ScrollToHashElement = ScrollToHashElement;
4189
4262
  exports.SidebarLayout = SidebarLayout;
4190
4263
  exports.Styled = Styled;
4191
4264
  exports.TableOfContents = TableOfContents;
@@ -4197,6 +4270,8 @@ exports.findFirstNode = findFirstNode;
4197
4270
  exports.isHttpOperation = isHttpOperation;
4198
4271
  exports.isHttpService = isHttpService;
4199
4272
  exports.isHttpWebhookOperation = isHttpWebhookOperation;
4273
+ exports.resolveRelativeLink = resolveRelativeLink;
4274
+ exports.resolveUrl = resolveUrl;
4200
4275
  exports.slugify = slugify;
4201
4276
  exports.useBundleRefsIntoDocument = useBundleRefsIntoDocument;
4202
4277
  exports.useParsedData = useParsedData;
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { __rest, __awaiter } from 'tslib';
2
2
  import * as React from 'react';
3
- import React__default, { useContext, memo, useMemo, useState, useEffect } from 'react';
3
+ import React__default, { useContext, memo, useMemo, useState, useEffect, useRef, useLayoutEffect } from 'react';
4
4
  import { convertToJsonSchema } from '@stoplight/http-spec/oas';
5
5
  import { resolveInlineRef, hasRef, isPlainObject as isPlainObject$1, safeParse, safeStringify } from '@stoplight/json';
6
6
  import isArray from 'lodash/isArray.js';
@@ -10,7 +10,7 @@ import { parse } from '@stoplight/yaml';
10
10
  import { isArray as isArray$1, Box, useBreakpoints, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, Callout, useModalState, Modal, ListBox, ListBoxItem, TabList, Tab, Tabs, TabPanels, TabPanel, Heading, useClipboard, Drawer, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
11
11
  import isObject from 'lodash/isObject.js';
12
12
  import { withErrorBoundary } from '@stoplight/react-error-boundary';
13
- import { useLocation, Link as Link$1, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
13
+ import { useLocation, Link as Link$1, BrowserRouter, MemoryRouter, HashRouter, useInRouterContext, Routes, Route } from 'react-router-dom';
14
14
  import { MarkdownViewer as MarkdownViewer$1, DefaultSMDComponents, MarkdownViewerProvider } from '@stoplight/markdown-viewer';
15
15
  export { DefaultSMDComponents } from '@stoplight/markdown-viewer';
16
16
  import cn from 'classnames';
@@ -51,8 +51,8 @@ import keys from 'lodash/keys.js';
51
51
  import sortBy from 'lodash/sortBy.js';
52
52
  import isNil from 'lodash/isNil.js';
53
53
  import omitBy from 'lodash/omitBy.js';
54
- import { HashLink } from 'react-router-hash-link';
55
54
  import { QueryClient, useQueryClient, QueryClientProvider } from 'react-query';
55
+ import { StaticRouter } from 'react-router-dom/server.js';
56
56
  import $RefParser from '@stoplight/json-schema-ref-parser';
57
57
  import * as PropTypes from 'prop-types';
58
58
  import isEqual from 'lodash/isEqual.js';
@@ -1105,6 +1105,9 @@ function slugify(name) {
1105
1105
  .replace(/^-/, '')
1106
1106
  .replace(/-$/, '');
1107
1107
  }
1108
+ const resolveRelativeLink = (slug) => {
1109
+ return slug ? slug.replace(/^\//, '') : '.';
1110
+ };
1108
1111
 
1109
1112
  const isApiKeySecurityScheme = (maybeIApiKey) => isObject(maybeIApiKey) && maybeIApiKey.type === 'apiKey';
1110
1113
  const isOAuth2SecurityScheme = (maybeIOAuth2) => isObject(maybeIOAuth2) && maybeIOAuth2.type === 'oauth2';
@@ -3501,7 +3504,6 @@ const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefaul
3501
3504
  const container = React.useRef(null);
3502
3505
  const child = React.useRef(null);
3503
3506
  const firstRender = useFirstRender();
3504
- const makeSlugAbsoluteRoute = useRouterType() == 'hash';
3505
3507
  React.useEffect(() => {
3506
3508
  setTimeout(() => {
3507
3509
  const scrollPosition = firstRender ? 'center' : 'nearest';
@@ -3522,7 +3524,7 @@ const TableOfContents = React.memo(({ tree, activeId, Link, maxDepthOpenByDefaul
3522
3524
  if (isDivider(item)) {
3523
3525
  return React.createElement(Divider, { key: key, item: item, isInResponsiveMode: isInResponsiveMode });
3524
3526
  }
3525
- return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3527
+ return (React.createElement(GroupItem, { key: key, item: item, depth: 0, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3526
3528
  }))))));
3527
3529
  });
3528
3530
  TableOfContents.displayName = 'TableOfContents';
@@ -3530,23 +3532,23 @@ const Divider = React.memo(({ item, isInResponsiveMode = false }) => {
3530
3532
  return (React.createElement(Box, { pl: 4, mb: 2, mt: 6, textTransform: "uppercase", fontSize: isInResponsiveMode ? 'lg' : 'sm', lineHeight: "relaxed", letterSpacing: "wide", fontWeight: "bold" }, item.title));
3531
3533
  });
3532
3534
  Divider.displayName = 'Divider';
3533
- const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, makeSlugAbsoluteRoute, onLinkClick }) => {
3535
+ const GroupItem = React.memo(({ item, depth, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick }) => {
3534
3536
  if (isExternalLink(item)) {
3535
3537
  return (React.createElement(Box, { as: "a", href: item.url, target: "_blank", rel: "noopener noreferrer", display: "block" },
3536
3538
  React.createElement(Item, { isInResponsiveMode: isInResponsiveMode, depth: depth, title: item.title, meta: React.createElement(Box, { as: Icon, icon: ['fas', 'external-link'] }) })));
3537
3539
  }
3538
3540
  else if (isGroup(item) || isNodeGroup(item)) {
3539
- return (React.createElement(Group, { depth: depth, item: item, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3541
+ return (React.createElement(Group, { depth: depth, item: item, maxDepthOpenByDefault: maxDepthOpenByDefault, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3540
3542
  }
3541
3543
  else if (isNode(item)) {
3542
- return (React.createElement(Node, { depth: depth, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute, item: item, onLinkClick: onLinkClick, meta: item.meta ? (React.createElement(Box, { color: NODE_META_COLOR[item.meta], textTransform: "uppercase", fontWeight: "medium" }, item.meta)) : (NODE_TYPE_META_ICON[item.type] && (React.createElement(Flex, { alignItems: "center" },
3544
+ return (React.createElement(Node, { depth: depth, isInResponsiveMode: isInResponsiveMode, item: item, onLinkClick: onLinkClick, meta: item.meta ? (React.createElement(Box, { color: NODE_META_COLOR[item.meta], textTransform: "uppercase", fontWeight: "medium" }, item.meta)) : (NODE_TYPE_META_ICON[item.type] && (React.createElement(Flex, { alignItems: "center" },
3543
3545
  item.version && React.createElement(Version, { value: item.version }),
3544
3546
  item.type !== 'model' && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_META_ICON[item.type] }))))) }));
3545
3547
  }
3546
3548
  return null;
3547
3549
  });
3548
3550
  GroupItem.displayName = 'GroupItem';
3549
- const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, makeSlugAbsoluteRoute, onLinkClick = () => { } }) => {
3551
+ const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMode, onLinkClick = () => { } }) => {
3550
3552
  const activeId = React.useContext(ActiveIdContext);
3551
3553
  const [isOpen, setIsOpen] = React.useState(() => isGroupOpenByDefault(depth, item, activeId, maxDepthOpenByDefault));
3552
3554
  const hasActive = !!activeId && hasActiveItem(item.items, activeId);
@@ -3574,7 +3576,7 @@ const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMo
3574
3576
  const showAsActive = hasActive && !isOpen;
3575
3577
  let elem;
3576
3578
  if (isNodeGroup(item)) {
3577
- elem = (React.createElement(Node, { depth: depth, item: item, meta: meta, showAsActive: showAsActive, onClick: handleClick, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3579
+ elem = (React.createElement(Node, { depth: depth, item: item, meta: meta, showAsActive: showAsActive, onClick: handleClick, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3578
3580
  }
3579
3581
  else {
3580
3582
  elem = (React.createElement(Item, { isInResponsiveMode: isInResponsiveMode, title: item.title, meta: meta, onClick: handleClick, depth: depth, isActive: showAsActive, icon: item.itemsType &&
@@ -3584,7 +3586,7 @@ const Group = React.memo(({ depth, item, maxDepthOpenByDefault, isInResponsiveMo
3584
3586
  elem,
3585
3587
  isOpen &&
3586
3588
  item.items.map((groupItem, key) => {
3587
- return (React.createElement(GroupItem, { key: key, item: groupItem, depth: depth + 1, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode, makeSlugAbsoluteRoute: makeSlugAbsoluteRoute }));
3589
+ return (React.createElement(GroupItem, { key: key, item: groupItem, depth: depth + 1, onLinkClick: onLinkClick, isInResponsiveMode: isInResponsiveMode }));
3588
3590
  })));
3589
3591
  });
3590
3592
  Group.displayName = 'Group';
@@ -3598,7 +3600,7 @@ const Item = React.memo(({ depth, isActive, id, title, meta, icon, isInResponsiv
3598
3600
  React.createElement(Flex, { alignItems: "center", fontSize: isInResponsiveMode ? 'base' : 'xs' }, meta)));
3599
3601
  });
3600
3602
  Item.displayName = 'Item';
3601
- const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, makeSlugAbsoluteRoute, onClick, onLinkClick = () => { } }) => {
3603
+ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode, onClick, onLinkClick = () => { } }) => {
3602
3604
  const activeId = React.useContext(ActiveIdContext);
3603
3605
  const isActive = activeId === item.slug || activeId === item.id;
3604
3606
  const LinkComponent = React.useContext(LinkContext);
@@ -3614,7 +3616,7 @@ const Node = React.memo(({ item, depth, meta, showAsActive, isInResponsiveMode,
3614
3616
  onClick(e, isActive ? undefined : true);
3615
3617
  }
3616
3618
  };
3617
- return (React.createElement(Box, { as: LinkComponent, to: makeSlugAbsoluteRoute && !item.slug.startsWith('/') ? `/${item.slug}` : item.slug, display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3619
+ return (React.createElement(Box, { as: LinkComponent, to: resolveRelativeLink(item.slug), display: "block", textDecoration: "no-underline", className: "ElementsTableOfContentsItem" },
3618
3620
  React.createElement(Item, { id: getHtmlIdFromItemId(item.slug || item.id), isActive: isActive || showAsActive, depth: depth, title: item.title, icon: NODE_TYPE_TITLE_ICON[item.type] && (React.createElement(Box, { as: Icon, color: NODE_TYPE_ICON_COLOR[item.type], icon: NODE_TYPE_TITLE_ICON[item.type] })), meta: meta, isInResponsiveMode: isInResponsiveMode, onClick: handleClick })));
3619
3621
  });
3620
3622
  Node.displayName = 'Node';
@@ -3879,7 +3881,66 @@ const ReactRouterMarkdownLink = ({ title, to, href: _href, children, }) => {
3879
3881
  if (isExternal) {
3880
3882
  return (React__default.createElement("a", { target: "_blank", rel: "noreferrer noopener", href: href, title: title }, children));
3881
3883
  }
3882
- return (React__default.createElement(HashLink, { to: href, title: title }, children));
3884
+ return (React__default.createElement("a", { href: href, title: title }, children));
3885
+ };
3886
+
3887
+ const ScrollToHashElement = ({ behavior = 'auto', initialBehavior = 'auto', inline = 'nearest', block = 'start', }) => {
3888
+ const [hash, setHash] = useState(window.location.hash);
3889
+ const [count, setCount] = useState(0);
3890
+ const originalListeners = useRef({});
3891
+ const [firstRun, setFirstRun] = useState(true);
3892
+ useEffect(() => setFirstRun(false), []);
3893
+ useEffect(() => {
3894
+ const handleLocationChange = () => {
3895
+ setHash(window.location.hash);
3896
+ setCount((count) => count + 1);
3897
+ };
3898
+ const onPopState = () => {
3899
+ window.dispatchEvent(new Event('locationchange'));
3900
+ };
3901
+ const addWindowListeners = () => {
3902
+ originalListeners.current.pushState = window.history.pushState;
3903
+ originalListeners.current.replaceState = window.history.replaceState;
3904
+ window.history.pushState = function (...args) {
3905
+ const result = originalListeners.current.pushState.apply(this, args);
3906
+ window.dispatchEvent(new Event('pushstate'));
3907
+ window.dispatchEvent(new Event('locationchange'));
3908
+ return result;
3909
+ };
3910
+ window.history.replaceState = function (...args) {
3911
+ const result = originalListeners.current.replaceState.apply(this, args);
3912
+ window.dispatchEvent(new Event('replacestate'));
3913
+ window.dispatchEvent(new Event('locationchange'));
3914
+ return result;
3915
+ };
3916
+ window.addEventListener('popstate', onPopState);
3917
+ window.addEventListener('locationchange', handleLocationChange);
3918
+ };
3919
+ const removeWindowListeners = () => {
3920
+ window.history.pushState = originalListeners.current.pushState;
3921
+ window.history.replaceState = originalListeners.current.replaceState;
3922
+ window.removeEventListener('popstate', onPopState);
3923
+ window.removeEventListener('locationchange', handleLocationChange);
3924
+ };
3925
+ addWindowListeners();
3926
+ return removeWindowListeners;
3927
+ }, []);
3928
+ useLayoutEffect(() => {
3929
+ const removeHashCharacter = (str) => {
3930
+ return str.slice(1);
3931
+ };
3932
+ if (hash) {
3933
+ const element = document.getElementById(removeHashCharacter(hash));
3934
+ if (element) {
3935
+ element.scrollIntoView({
3936
+ behavior: firstRun ? initialBehavior : behavior,
3937
+ inline: inline,
3938
+ block: block,
3939
+ });
3940
+ }
3941
+ }
3942
+ }, [hash, count, firstRun]);
3943
+ return null;
3883
3944
  };
3884
3945
 
3885
3946
  const NonIdealState = ({ description, icon, title }) => {
@@ -3959,18 +4020,29 @@ const components = {
3959
4020
  return React.createElement(LinkHeading, Object.assign({ size: 4 }, props));
3960
4021
  },
3961
4022
  };
4023
+ const InternalRoutes = ({ children }) => {
4024
+ return (React.createElement(Routes, null,
4025
+ React.createElement(Route, { path: "/*", element: React.createElement(MarkdownComponentsProvider, { value: components },
4026
+ React.createElement(ScrollToHashElement, null),
4027
+ children) })));
4028
+ };
3962
4029
  function withRouter(WrappedComponent) {
3963
4030
  const WithRouter = (props) => {
3964
4031
  var _a, _b, _c;
4032
+ const outerRouter = useInRouterContext();
3965
4033
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
3966
4034
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
3967
4035
  const routerType = (_c = props.router) !== null && _c !== void 0 ? _c : 'history';
3968
4036
  const { Router, routerProps } = useRouter(routerType, basePath, staticRouterPath);
4037
+ if (!outerRouter) {
4038
+ return (React.createElement(RouterTypeContext.Provider, { value: routerType },
4039
+ React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
4040
+ React.createElement(InternalRoutes, null,
4041
+ React.createElement(WrappedComponent, Object.assign({}, props, { outerRouter: false }))))));
4042
+ }
3969
4043
  return (React.createElement(RouterTypeContext.Provider, { value: routerType },
3970
- React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
3971
- React.createElement(Route, { path: "/" },
3972
- React.createElement(MarkdownComponentsProvider, { value: components },
3973
- React.createElement(WrappedComponent, Object.assign({}, props)))))));
4044
+ React.createElement(InternalRoutes, null,
4045
+ React.createElement(WrappedComponent, Object.assign({}, props, { outerRouter: true })))));
3974
4046
  };
3975
4047
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
3976
4048
  return WithRouter;
@@ -4140,4 +4212,4 @@ const createElementClass = (Component, propDescriptors) => {
4140
4212
  };
4141
4213
  };
4142
4214
 
4143
- export { DeprecatedBadge, Docs, ElementsOptionsProvider, ExportButton, HttpMethodColors, InlineRefResolverProvider, LinkHeading, Logo, MarkdownComponentsProvider, MockingProvider, NodeTypeColors, NodeTypeIconDefs, NodeTypePrettyName, NonIdealState, ParsedDocs, PersistenceContextProvider, PoweredByLink, ReactRouterMarkdownLink, ResponsiveSidebarLayout, RouterTypeContext, SidebarLayout, Styled, TableOfContents, TryIt, TryItWithRequestSamples, createElementClass, createResolvedObject, findFirstNode, isHttpOperation, isHttpService, isHttpWebhookOperation, slugify, useBundleRefsIntoDocument, useParsedData, useParsedValue, useResponsiveLayout, useRouter, withMosaicProvider, withPersistenceBoundary, withQueryClientProvider, withRouter, withStyles };
4215
+ export { DeprecatedBadge, Docs, ElementsOptionsProvider, ExportButton, HttpMethodColors, InlineRefResolverProvider, LinkHeading, Logo, MarkdownComponentsProvider, MockingProvider, NodeTypeColors, NodeTypeIconDefs, NodeTypePrettyName, NonIdealState, ParsedDocs, PersistenceContextProvider, PoweredByLink, ReactRouterMarkdownLink, ResponsiveSidebarLayout, RouterTypeContext, ScrollToHashElement, SidebarLayout, Styled, TableOfContents, TryIt, TryItWithRequestSamples, createElementClass, createResolvedObject, findFirstNode, isHttpOperation, isHttpService, isHttpWebhookOperation, resolveRelativeLink, resolveUrl, slugify, useBundleRefsIntoDocument, useParsedData, useParsedValue, useResponsiveLayout, useRouter, withMosaicProvider, withPersistenceBoundary, withQueryClientProvider, withRouter, withStyles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoplight/elements-core",
3
- "version": "8.5.2",
3
+ "version": "9.0.0",
4
4
  "main": "./index.js",
5
5
  "sideEffects": [
6
6
  "web-components.min.js",
@@ -46,8 +46,7 @@
46
46
  "nanoid": "^3.1.32",
47
47
  "prop-types": "^15.7.2",
48
48
  "react-query": "^3.34.19",
49
- "react-router-dom": "^5.2.0",
50
- "react-router-hash-link": "^2.1.0",
49
+ "react-router-dom": "^6.28.0",
51
50
  "tslib": "^2.1.0",
52
51
  "urijs": "^1.19.11",
53
52
  "util": "^0.12.4",
package/utils/string.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  /// <reference types="lodash" />
2
2
  export declare const caseInsensitivelyEquals: import("lodash").CurriedFunction2<string, string, boolean>;
3
3
  export declare function slugify(name: string): string;
4
+ export declare const resolveRelativeLink: (slug?: string) => string;