Skip to content

Commit 41ebfa1

Browse files
committed
Fix clicking methods from search not setting being highlighted, arrow to first method in search
1 parent 62fb584 commit 41ebfa1

File tree

4 files changed

+67
-55
lines changed

4 files changed

+67
-55
lines changed

src/ApiParameter.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
</template>
5959

6060
<script setup lang="ts">
61-
import type { ApiMethod, ApiMethodParameter } from './interfaces';
6261
import { useId } from 'vue';
62+
import type { ApiMethod, ApiMethodParameter } from './interfaces';
6363
6464
const labelId = useId();
6565

src/App.ts

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import HighlightedSearchMethod from './HighlightedSearchMethod';
55
import type { ApiInterface, ApiMethod, ApiMethodParameter, ApiServices, SidebarGroupData } from './interfaces';
66
import { ApiSearcher } from './search';
77

8+
const sidebar = ref<HTMLElement | null>(null);
89
const inputSearch = ref<HTMLInputElement | null>(null);
910
const inputApiKey = ref<HTMLInputElement | null>(null);
1011
const inputAccessToken = ref<HTMLInputElement | null>(null);
@@ -88,6 +89,7 @@ export default defineComponent({
8889
format: 'json',
8990
favorites: new Set<string>(),
9091
},
92+
skipNextHashChange: false,
9193
keyInputType: 'password',
9294
hasValidWebApiKey: false,
9395
hasValidAccessToken: false,
@@ -105,6 +107,7 @@ export default defineComponent({
105107
},
106108
setup() {
107109
return {
110+
sidebar,
108111
inputSearch,
109112
inputApiKey,
110113
inputAccessToken,
@@ -167,25 +170,18 @@ export default defineComponent({
167170
localStorage.removeItem('steamid');
168171
}
169172
},
170-
currentInterface(newInterface: string): void {
171-
if (newInterface) {
172-
document.title = `${newInterface} – Steam Web API Documentation`;
173-
} else {
174-
document.title = `Steam Web API Documentation`;
175-
}
176-
177-
if (document.scrollingElement) {
178-
document.scrollingElement.scrollTop = 0;
179-
}
180-
},
181173
currentFilter(newFilter: string, oldFilter: string): void {
182174
if (!newFilter) {
183175
this.$nextTick(this.scrollInterfaceIntoView);
176+
177+
if (oldFilter) {
178+
this.sidebar!.scrollTop = 0;
179+
}
184180
} else {
185-
this.currentInterface = '';
181+
this.setInterface('');
186182

187183
if (!oldFilter) {
188-
document.querySelector('.sidebar')!.scrollTop = 0;
184+
this.sidebar!.scrollTop = 0;
189185
}
190186
}
191187
},
@@ -215,12 +211,19 @@ export default defineComponent({
215211
console.error(e);
216212
}
217213

218-
this.setInterface();
214+
if (location.hash.startsWith('#')) {
215+
this.setInterface(location.hash.substring(1), true);
216+
}
219217

220218
window.addEventListener(
221219
'hashchange',
222220
() => {
223-
this.setInterface();
221+
if (this.skipNextHashChange) {
222+
this.skipNextHashChange = false;
223+
return;
224+
}
225+
226+
this.setInterface(location.hash.substring(1));
224227
},
225228
false,
226229
);
@@ -284,42 +287,46 @@ export default defineComponent({
284287
},
285288
},
286289
methods: {
287-
setInterface(): void {
288-
let currentInterface = location.hash;
289-
let currentMethod = '';
290-
291-
if (currentInterface[0] === '#') {
292-
const split = currentInterface.substring(1).split('/', 2);
293-
currentInterface = split[0];
294-
295-
if (split[1]) {
296-
currentMethod = split[1];
297-
}
298-
}
290+
setInterface(interfaceAndMethod: string, setFromUrl = false): void {
291+
const split = interfaceAndMethod.split('/', 2);
292+
let currentInterface: string | null = split[0];
293+
let currentMethod: string | null = split.length > 1 ? split[1] : null;
299294

300295
if (!Object.hasOwn(this.interfaces, currentInterface)) {
301-
currentInterface = '';
302-
currentMethod = '';
303-
} else if (!Object.hasOwn(this.interfaces[currentInterface], currentMethod)) {
304-
currentMethod = '';
296+
currentInterface = null;
297+
currentMethod = null;
298+
} else if (currentMethod !== null && !Object.hasOwn(this.interfaces[currentInterface], currentMethod)) {
299+
currentMethod = null;
305300
}
306301

307-
const interfaceChanged = this.currentInterface !== currentInterface;
302+
this.currentInterface = currentInterface || '';
308303

309-
this.currentInterface = currentInterface;
304+
if (currentInterface) {
305+
document.title = `${currentInterface} – Steam Web API Documentation`;
306+
} else {
307+
document.title = `Steam Web API Documentation`;
308+
}
310309

311-
if (interfaceChanged) {
312-
// Have to scroll manually because location.hash doesn't exist in DOM as target yet
313-
this.$nextTick(() => {
314-
const element = document.getElementById(`${currentInterface}/${currentMethod}`);
310+
// Since we won't scroll to a method, scroll to top (as there is no element with just interface id)
311+
if (document.scrollingElement && !currentMethod) {
312+
document.scrollingElement.scrollTop = 0;
313+
}
315314

316-
if (element) {
317-
element.scrollIntoView({
318-
block: 'start',
319-
});
320-
}
321-
});
315+
if (setFromUrl) {
316+
return;
322317
}
318+
319+
this.$nextTick(() => {
320+
this.skipNextHashChange = true;
321+
322+
if (currentMethod) {
323+
location.hash = `#${currentInterface}/${currentMethod}`;
324+
} else if (currentInterface) {
325+
location.hash = `#${currentInterface}`;
326+
} else {
327+
location.hash = '';
328+
}
329+
});
323330
},
324331
fillSteamidParameter(): void {
325332
if (!this.userData.steamid) {
@@ -590,19 +597,23 @@ export default defineComponent({
590597
localStorage.setItem('favorites', JSON.stringify([...this.userData.favorites]));
591598
},
592599
navigateSidebar(direction: number): void {
593-
const keys = Object.keys(this.filteredInterfaces);
594-
595-
const size = keys.length;
596-
const index = keys.indexOf(this.currentInterface) + direction;
600+
const entries = Object.entries(this.filteredInterfaces);
601+
const index = entries.findIndex((x) => x[0] === this.currentInterface) + direction;
602+
const size = entries.length;
603+
const [interfaceName, methods] = entries[((index % size) + size) % size];
604+
const firstMethodName = Object.keys(methods)[0];
597605

598-
this.currentInterface = keys[((index % size) + size) % size];
606+
this.setInterface(`${interfaceName}/${firstMethodName}`);
599607
this.scrollInterfaceIntoView();
608+
609+
// This is trash, but the focus gets lost because of location.hash change
610+
this.$nextTick(() => {
611+
this.inputSearch?.focus();
612+
});
600613
},
601614
focusApiKey(): void {
602-
location.hash = '';
603-
604-
this.currentInterface = '';
605615
this.currentFilter = '';
616+
this.setInterface('');
606617

607618
this.$nextTick(() => {
608619
const element = this.hasValidAccessToken ? this.inputAccessToken : this.inputApiKey;
@@ -612,7 +623,7 @@ export default defineComponent({
612623
}
613624
});
614625
},
615-
onSearchInput(e: InputEvent) {
626+
onSearchInput(e: Event) {
616627
requestAnimationFrame(() => {
617628
this.currentFilter = (e.target as HTMLInputElement).value;
618629
});

src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
<div class="container">
2929
<div class="row">
30-
<div class="col-lg-3 sidebar py-3" role="navigation">
30+
<div class="col-lg-3 sidebar py-3" role="navigation" ref="sidebar">
3131
<details class="interface-list-container" open v-if="userData.favorites.size > 0 && !currentFilter">
3232
<summary class="interface-group-name">Your favorites</summary>
3333

src/style.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,11 @@ body {
117117
color: #e5e5e5;
118118
border: 0;
119119
border-radius: 0;
120+
transition: box-shadow 0.2s;
120121
}
121122

122123
.card:target {
123-
outline: 5px solid #096295;
124+
box-shadow: 0 0 0 5px #66c0f4;
124125
}
125126

126127
.card-header {

0 commit comments

Comments
 (0)