feat(): Fix navigation with # in markdown

This commit is contained in:
Depickere Sven 2023-04-25 15:35:03 +01:00
parent 1a9cb581bf
commit e34d42d73d
3 changed files with 47 additions and 12 deletions

View File

@ -74,9 +74,9 @@ export class AppStore {
// override the openApi standard to version 3.1.0 // override the openApi standard to version 3.1.0
// TODO remove when fully supporting open API 3.1.0 // TODO remove when fully supporting open API 3.1.0
spec.openapi = "3.1.0"; spec.openapi = '3.1.0';
this.spec = new SpecStore(spec, specUrl, this.options); this.spec = new SpecStore(spec, specUrl, this.options);
this.menu = new MenuStore(this.spec, this.scroll, this.history); this.menu = new MenuStore(this.spec, this.scroll, this.history, this.options);
if (!this.options.disableSearch) { if (!this.options.disableSearch) {
this.search = new SearchStore(); this.search = new SearchStore();

View File

@ -18,7 +18,13 @@ export class HistoryService {
get currentId(): string { get currentId(): string {
if (IS_BROWSER) { if (IS_BROWSER) {
if (this.shouldQueryParamNavigationBeUsed()) { if (this.shouldQueryParamNavigationBeUsed()) {
return this.getQueryParams(window.location.search); // When the window.location.hash is not empty this means that we have clicked on
// router that's for example stored in the description via markdown
if (window.location.hash == '') {
return this.getQueryParams(window.location.search);
} else {
return decodeURIComponent(window.location.hash.substring(1));
}
} else { } else {
return decodeURIComponent(window.location.hash.substring(1)); return decodeURIComponent(window.location.hash.substring(1));
} }
@ -65,7 +71,13 @@ export class HistoryService {
return; return;
} }
if (id == null || id === this.currentId) { // If there currentId and the ID are equal but there is still
// a hash left when using query param navigation
// that means that the URL hasn't been overridden
if (
id == null ||
(id === this.currentId && this.checkIfThereIsHashLeftWhenQueryParamNavigationShouldBeUsed())
) {
return; return;
} }
if (rewriteHistory) { if (rewriteHistory) {
@ -103,6 +115,8 @@ export class HistoryService {
private getFullUrl(id: string): string { private getFullUrl(id: string): string {
const url = this.getUrl(); const url = this.getUrl();
// Override the hash so it's removed when using query param navigation
url.hash = '';
url.searchParams.set('redoc', id); url.searchParams.set('redoc', id);
return url.toString(); return url.toString();
} }
@ -111,9 +125,7 @@ export class HistoryService {
return new URL(window.location.href); return new URL(window.location.href);
} }
// private getQueryParamKey(): void { private checkIfThereIsHashLeftWhenQueryParamNavigationShouldBeUsed(): boolean {
// let searchParams = new URLSearchParams(window.location.search); return !(this.shouldQueryParamNavigationBeUsed() && window.location.hash != '');
// searchParams.get('redoc') }
//
// }
} }

View File

@ -8,6 +8,7 @@ import { GROUP_DEPTH } from './MenuBuilder';
import type { SpecStore } from './models'; import type { SpecStore } from './models';
import type { ScrollService } from './ScrollService'; import type { ScrollService } from './ScrollService';
import type { IMenuItem } from './types'; import type { IMenuItem } from './types';
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
/** Generic interface for MenuItems */ /** Generic interface for MenuItems */
@ -42,6 +43,7 @@ export class MenuStore {
items: IMenuItem[]; items: IMenuItem[];
flatItems: IMenuItem[]; flatItems: IMenuItem[];
private options: RedocNormalizedOptions;
/** /**
* cached flattened menu items to support absolute indexing * cached flattened menu items to support absolute indexing
@ -53,11 +55,19 @@ export class MenuStore {
* *
* @param spec [SpecStore](#SpecStore) which contains page content structure * @param spec [SpecStore](#SpecStore) which contains page content structure
* @param scroll scroll service instance used by this menu * @param scroll scroll service instance used by this menu
* @param history the history service
* @param options the RedocNormalizedOptions that can be used to retrieve the config options
*/ */
constructor(spec: SpecStore, public scroll: ScrollService, public history: HistoryService) { constructor(
spec: SpecStore,
public scroll: ScrollService,
public history: HistoryService,
options: RedocNormalizedOptions,
) {
makeObservable(this); makeObservable(this);
this.items = spec.contentItems; this.items = spec.contentItems;
this.options = options;
this.flatItems = flattenByProp(this.items || [], 'items'); this.flatItems = flattenByProp(this.items || [], 'items');
this.flatItems.forEach((item, idx) => (item.absoluteIdx = idx)); this.flatItems.forEach((item, idx) => (item.absoluteIdx = idx));
@ -126,16 +136,28 @@ export class MenuStore {
item = this.flatItems.find(i => i.id === id); item = this.flatItems.find(i => i.id === id);
if (item) { if (item) {
this.activateAndScroll(item, false); this.activateAndScrollWithNavigationStrategy(item);
} else { } else {
if (id.startsWith(SECURITY_SCHEMES_SECTION_PREFIX)) { if (id.startsWith(SECURITY_SCHEMES_SECTION_PREFIX)) {
item = this.flatItems.find(i => SECURITY_SCHEMES_SECTION_PREFIX.startsWith(i.id)); item = this.flatItems.find(i => SECURITY_SCHEMES_SECTION_PREFIX.startsWith(i.id));
this.activateAndScroll(item, false); this.activateAndScrollWithNavigationStrategy(item);
} }
this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(id)}"]`); this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(id)}"]`);
} }
}; };
private activateAndScrollWithNavigationStrategy(item: IMenuItem | undefined): void {
if (this.shouldQueryParamNavigationBeUsed()) {
this.activateAndScroll(item, true, true);
} else {
this.activateAndScroll(item, false);
}
}
private shouldQueryParamNavigationBeUsed(): boolean {
return this.options?.userQueryParamToNavigate;
}
/** /**
* get section/operation DOM Node related to the item or null if it doesn't exist * get section/operation DOM Node related to the item or null if it doesn't exist
* @param idx item absolute index * @param idx item absolute index
@ -237,6 +259,7 @@ export class MenuStore {
) { ) {
// item here can be a copy from search results so find corresponding item from menu // item here can be a copy from search results so find corresponding item from menu
const menuItem = (item && this.getItemById(item.id)) || item; const menuItem = (item && this.getItemById(item.id)) || item;
console.log('activateAndScroll', menuItem?.id, updateLocation, rewriteHistory);
this.activate(menuItem, updateLocation, rewriteHistory); this.activate(menuItem, updateLocation, rewriteHistory);
this.scrollToActive(); this.scrollToActive();
if (!menuItem || !menuItem.items.length) { if (!menuItem || !menuItem.items.length) {