more docs
This commit is contained in:
parent
7437911e9e
commit
49e1da1cbd
13 changed files with 505 additions and 23 deletions
|
|
@ -1,8 +1,8 @@
|
|||
still left:
|
||||
- the last 3 stateManager docs,
|
||||
- all the base client methods,
|
||||
- all the modals
|
||||
- all the enums
|
||||
- all the evnts(listeners)
|
||||
- further docs on streamsync
|
||||
- how to correctly load a map
|
||||
- how to handle scores (sec. 6)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# All of the methods of the StateManager
|
||||
The table below shows all of the functions of the State manager. From this point onwards `taClient` will be defined as `let taClient = new TAClient();`.
|
||||
|
||||
Below you can find a table that shows all of the methods of the stateManager.
|
||||
| taClient.stateManager. (method) | Purpose and functionality | Is Async |
|
||||
|
|
@ -19,20 +20,3 @@ Below you can find a table that shows all of the methods of the stateManager.
|
|||
|once()|This is essentially the same as `taClient.stateManager.on()` except it unsubscribes after the first event.|False|
|
||||
|removeListener()|Unsibscribe from the events that you have started to listen to.|False|
|
||||
|
||||
## How to listen, what is the general schema?
|
||||
Generally, the format is just:
|
||||
```ts
|
||||
taClient.stateManager.on('eventType', () => {
|
||||
// callback function
|
||||
})
|
||||
```
|
||||
or
|
||||
```ts
|
||||
async function handleEvent(params) {
|
||||
// code here
|
||||
}
|
||||
|
||||
taClient.stateManager.on('eventType', handleEvent);
|
||||
```
|
||||
|
||||
All of the event types and how to handle them are within this State Manager section.
|
||||
88
src/lib/taDocs/2-stateManager-on.md
Normal file
88
src/lib/taDocs/2-stateManager-on.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# The stateManager.on() method
|
||||
This documents the `stateManager.on()` method.
|
||||
|
||||
There are multiple parameters for this method, and a deeper dive into each of them can be found in the [Events -> Introduction page](/documentation#Events/6-events-intro) and all of the pages within the Events category.
|
||||
|
||||
|
||||
## How to listen, what is the general schema?
|
||||
Generally, the format is just:
|
||||
```ts
|
||||
taClient.stateManager.on('eventType', () => {
|
||||
// callback function
|
||||
})
|
||||
```
|
||||
or
|
||||
```ts
|
||||
async function handleEvent(params) {
|
||||
// code here
|
||||
}
|
||||
|
||||
taClient.stateManager.on('eventType', handleEvent);
|
||||
```
|
||||
|
||||
Essentiallly both are the same thing. The point is that when an event happens, depending on your subscription type, different types of data will be passed through to a method(callback). Once you start to listen to events, you will listen until you *remove* the listener or *disconnect* the client.
|
||||
|
||||
Find documentation on how to remove a listener at the [State Manager -> RemoveListener page](/documentation#State%20Manager/2-stateManager-removeListener)
|
||||
|
||||
I will provide an example from ShyyTAUI which listens to the `matchDeleted` and `matchUpdated` events.
|
||||
```ts
|
||||
// Handle match events
|
||||
// Handle when a match is updated. This happens when a new map is set, or something about the match chages like authorised users
|
||||
async function handleMatchUpdated(params: [Match, Tournament]) {
|
||||
// Log the request for debugging and clarity
|
||||
console.log("Match updated:", params);
|
||||
// Check if the updated match guid is the same as the match we are in-s guid
|
||||
if (params[0].guid === matchGuid) {
|
||||
// If it is, update the match variable to ensure that we see the latest data
|
||||
match = params[0];
|
||||
// Since the song might have changed, let's refresh the song data(from BeatSaver)
|
||||
await refreshMatchDetails(params[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle when a match is deleted
|
||||
function handleMatchDeleted(params: [Match, Tournament]) {
|
||||
// Just log the packet in console to ensure that I know what is happening
|
||||
console.log("Match deleted:", params);
|
||||
// Check if the match that got deleted was the one the user was just in
|
||||
if (params[0].guid === matchGuid) {
|
||||
// If yes then navigate back to the create or join match screen without resetting the state
|
||||
goto(`/tournaments/${tournamentGuid}`);
|
||||
}
|
||||
}
|
||||
|
||||
// This is triggered when the component is loaded
|
||||
onMount(async() => {
|
||||
if ($authTokenStore) {
|
||||
// Using stateManger listen to global changes to our match
|
||||
client.stateManager.on('matchDeleted', handleMatchDeleted);
|
||||
client.stateManager.on('matchUpdated', handleMatchUpdated);
|
||||
} else {
|
||||
window.location.href = "/discordAuth"
|
||||
}
|
||||
});
|
||||
|
||||
// This is triggered when the component is destroyed
|
||||
onDestroy(() => {
|
||||
// Remove the listeners that were added on mount
|
||||
client.stateManager.removeListener('matchUpdated', handleMatchUpdated);
|
||||
client.stateManager.removeListener('matchDeleted', handleMatchDeleted);
|
||||
});
|
||||
```
|
||||
<div class="info-box">
|
||||
<span class="material-icons">info</span>
|
||||
<div>
|
||||
<strong>Example:</strong> This is an example from the page of ShyyTAUI within the match cordination page.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
As you may see, the `params` variable in the method / callback had the same type in the `matchUpdated` and `matchDeleted` case. Generally it is very easy to even guess what the packet should include, as it of course includes the details of whta its name entails. In this case it was a simple array with a `Match` and `Tournament` object inside it.
|
||||
|
||||
<div class="warning-box">
|
||||
<span class="material-icons">warning</span>
|
||||
<div>
|
||||
<strong>IMPORTANT:</strong> When you listen to events, rather than only listening in the current tournament, it listens globally!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
The above warning is important to consider. Since this is the `stateManager`, it will receive updates for the global state. That includes any tournament-s details that you are authorised to view. So say you are in *Tournament 1*, and then get a `matchUpdated` packet. This DOES NOT mean that the match you are currently in for updated. It means that a match (the object of which is passed on in `params[0]`) has been updated in the tournament(the object of which is passed on in `params[1]`). This tournament can however be *Tournament 57* as far as we are concerned. You must have authorisation for the tournament in order to actually receive these events. Although even in my implementation I discard this `Tournament` object, it can prove useful a lot of times. An example of this is when a user disconnects. You want to know if that user was in your tournament or not.
|
||||
36
src/lib/taDocs/2-stateManager-once.md
Normal file
36
src/lib/taDocs/2-stateManager-once.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# The stateManager.once() method
|
||||
This documents the `stateManager.once()` method.
|
||||
|
||||
There are multiple parameters for this method, and a deeper dive into each of them can be found in the [Events -> Introduction page](/documentation#Events/6-events-intro) and all of the pages within the Events category.
|
||||
|
||||
<div class="info-box">
|
||||
<span class="material-icons">info</span>
|
||||
<div>
|
||||
<strong>In Short:</strong> The difference between on() and once() is that on() stays connected until removed or the client disconnects, while once will detach after the first event.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## How is this different from stateManager.on()?
|
||||
Generally, the format is just:
|
||||
```ts
|
||||
taClient.stateManager.once('eventType', () => {
|
||||
// callback function
|
||||
})
|
||||
```
|
||||
or
|
||||
```ts
|
||||
async function handleEvent(params) {
|
||||
// code here
|
||||
}
|
||||
|
||||
taClient.stateManager.once('eventType', handleEvent);
|
||||
```
|
||||
|
||||
Essentiallly both are the same thing. The point is that when an event happens, depending on your subscription type, different types of data will be passed through to a method(callback). The reason why `once()` is different is because it automatically detaches after the first event. That means the callback method is only triggered once, and then the listener is removed.
|
||||
|
||||
The listener also detaches upon a disconnect from the `CoreServer`.
|
||||
|
||||
## Its events
|
||||
The events in `once()` are the exact same as in `on()`. There is no direct implementation in ShyyTAUI, as there really is not a scenario when you want a listener to only trigger once.
|
||||
|
||||
You might wonder why that is. Why is it not enough to listen to a `matchDeleted` event once? The answer is simple. Since just like `on()`, `once()` also gets triggered by other tournaments' data changes and not just the current match getting deleted.
|
||||
79
src/lib/taDocs/2-stateManager-removeListener.md
Normal file
79
src/lib/taDocs/2-stateManager-removeListener.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# The stateManager.removeListener() method
|
||||
This documents the `stateManager.removeListener()` method.
|
||||
|
||||
There are multiple parameters for this method, and a deeper dive into each of them can be found in the [Events -> Introduction page](/documentation#Events/6-events-intro) and all of the pages within the Events category.
|
||||
|
||||
|
||||
## How can you remove a listener?
|
||||
Generally, the format is just:
|
||||
```ts
|
||||
taClient.stateManager.removeListener('eventType', () => {
|
||||
// callback function
|
||||
})
|
||||
```
|
||||
or
|
||||
```ts
|
||||
async function handleEvent(params) {
|
||||
// code here
|
||||
}
|
||||
|
||||
taClient.stateManager.removeListener('eventType', handleEvent);
|
||||
```
|
||||
|
||||
Essentiallly both are the same thing. The point is that they both remove a listener added by `taClient.stateManager.on()` or `taClient.stateManager.once()`.
|
||||
|
||||
Find documentation on how to add a listener at the [State Manager -> On page](/documentation#State%20Manager/2-stateManager-on)
|
||||
|
||||
I will provide an example from ShyyTAUI which listens to the `matchDeleted` and `matchUpdated` events.
|
||||
```ts
|
||||
// This is triggered when the component is destroyed
|
||||
onDestroy(() => {
|
||||
// Remove the listeners that were added on mount
|
||||
client.stateManager.removeListener('matchUpdated', handleMatchUpdated);
|
||||
client.stateManager.removeListener('matchDeleted', handleMatchDeleted);
|
||||
});
|
||||
|
||||
// This is triggered when the component is loaded
|
||||
onMount(async() => {
|
||||
if ($authTokenStore) {
|
||||
// Using stateManger listen to global changes to our match
|
||||
client.stateManager.on('matchDeleted', handleMatchDeleted);
|
||||
client.stateManager.on('matchUpdated', handleMatchUpdated);
|
||||
} else {
|
||||
window.location.href = "/discordAuth"
|
||||
}
|
||||
});
|
||||
|
||||
// Handle match events
|
||||
// Handle when a match is updated. This happens when a new map is set, or something about the match chages like authorised users
|
||||
async function handleMatchUpdated(params: [Match, Tournament]) {
|
||||
// Log the request for debugging and clarity
|
||||
console.log("Match updated:", params);
|
||||
// Check if the updated match guid is the same as the match we are in-s guid
|
||||
if (params[0].guid === matchGuid) {
|
||||
// If it is, update the match variable to ensure that we see the latest data
|
||||
match = params[0];
|
||||
// Since the song might have changed, let's refresh the song data(from BeatSaver)
|
||||
await refreshMatchDetails(params[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle when a match is deleted
|
||||
function handleMatchDeleted(params: [Match, Tournament]) {
|
||||
// Just log the packet in console to ensure that I know what is happening
|
||||
console.log("Match deleted:", params);
|
||||
// Check if the match that got deleted was the one the user was just in
|
||||
if (params[0].guid === matchGuid) {
|
||||
// If yes then navigate back to the create or join match screen without resetting the state
|
||||
goto(`/tournaments/${tournamentGuid}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
<div class="info-box">
|
||||
<span class="material-icons">info</span>
|
||||
<div>
|
||||
<strong>Example:</strong> This is an example from the page of ShyyTAUI within the match cordination page.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
You would usually want to use this method when you unload a component or switch screens. But there is not much mor eto say about it.
|
||||
31
src/lib/taDocs/3-client-intro.md
Normal file
31
src/lib/taDocs/3-client-intro.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# An Introduction to the Client
|
||||
The `taClient`(client for short) is how most of the actions will be carried out. It is used when you want to query the database, connect to servers, and listen to things that are not saved in the state, such as live data
|
||||
|
||||
The `stateManager` is the way you would fetch tournaments, get matches, or do anything related to getting specific data that does not require a database query. It is best if you are familiar with what methods it has and how you can use them.
|
||||
|
||||
For the ease of this explanation let me provide an example:
|
||||
```ts
|
||||
onMount(async() => {
|
||||
// Check if the user is logged in
|
||||
if ($authTokenStore) {
|
||||
// Using the client listen to events that will not be stored in the state, such as scores and song finish events
|
||||
client.on('realtimeScore', handleRealtimeScoreUpdate);
|
||||
client.on('songFinished', handleSongFinished);
|
||||
} else {
|
||||
// If the user is not authenticated, they will be thrown to the discord authentication page
|
||||
window.location.href = "/discordAuth"
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
// Remove the listeners that were added on mount
|
||||
client.removeListener('realtimeScore', handleRealtimeScoreUpdate);
|
||||
client.removeListener('songFinished', handleSongFinished);
|
||||
});
|
||||
```
|
||||
<div class="info-box">
|
||||
<span class="material-icons">info</span>
|
||||
<div>
|
||||
<strong>Example:</strong> This is an example from the page of ShyyTAUI within the match coorination page.
|
||||
</div>
|
||||
</div>
|
||||
0
src/lib/taDocs/4-modals-intro.md
Normal file
0
src/lib/taDocs/4-modals-intro.md
Normal file
0
src/lib/taDocs/5-enums-intro.md
Normal file
0
src/lib/taDocs/5-enums-intro.md
Normal file
0
src/lib/taDocs/6-events-intro.md
Normal file
0
src/lib/taDocs/6-events-intro.md
Normal file
|
|
@ -50,9 +50,76 @@
|
|||
category: "Client",
|
||||
icon: "api",
|
||||
items: [
|
||||
{ title: "3-", file: "", order: 1 },
|
||||
{ title: "Endpoints", file: "", order: 2 },
|
||||
{ title: "Response Types", file: "", order: 3 }
|
||||
{ title: "Introduction to the Client", file: "3-client-intro", order: 1 },
|
||||
{ title: "All Functions", file: "3-client-allFunctions", order: 2 },
|
||||
{ title: "AddAuthorizedUser", file: "3-client-addAuthorizedUser", order: 3 },
|
||||
{ title: "AddQualifierMaps", file: "3-client-addQualifierMaps", order: 4 },
|
||||
{ title: "AddServer", file: "3-client-addServer", order: 5 },
|
||||
{ title: "AddTournamentPool", file: "3-client-addTournamentPool", order: 6 },
|
||||
{ title: "AddTournamentPoolMaps", file: "3-client-addTournamentPoolMaps", order: 7 },
|
||||
{ title: "AddTournamentTeam", file: "3-client-addTournamentTeam", order: 8 },
|
||||
{ title: "AddUserToMatch", file: "3-client-addUserToMatch", order: 9 },
|
||||
{ title: "Connect", file: "3-client-connect", order: 10 },
|
||||
{ title: "CreateMatch", file: "3-client-createMatch", order: 11 },
|
||||
{ title: "CreateQualifierEvent", file: "3-client-createQualifierEvent", order: 12 },
|
||||
{ title: "CreateTournament", file: "3-client-createTournament", order: 13 },
|
||||
{ title: "DelayTestFinished", file: "3-client-delayTestFinished", order: 14 },
|
||||
{ title: "DeleteMatch", file: "3-client-deleteMatch", order: 15 },
|
||||
{ title: "DeleteQualifierEvent", file: "3-client-deleteQualifierEvent", order: 16 },
|
||||
{ title: "DeleteTournament", file: "3-client-deleteTournament", order: 17 },
|
||||
{ title: "Disconnect", file: "3-client-disconnect", order: 18 },
|
||||
{ title: "Emit", file: "3-client-emit", order: 19 },
|
||||
{ title: "FlipColors", file: "3-client-flipColors", order: 20 },
|
||||
{ title: "FlipHands", file: "3-client-flipHands", order: 21 },
|
||||
{ title: "GenerateBotToken", file: "3-client-generateBotToken", order: 22 },
|
||||
{ title: "GetAuthorizedUsers", file: "3-client-getAuthorizedUsers", order: 23 },
|
||||
{ title: "GetBotTokensForUser", file: "3-client-getBotTokensForUser", order: 24 },
|
||||
{ title: "GetDiscordInfo", file: "3-client-getDiscordInfo", order: 25 },
|
||||
{ title: "GetLeaderboard", file: "3-client-getLeaderboard", order: 26 },
|
||||
{ title: "IsConnected", file: "3-client-isConnected", order: 27 },
|
||||
{ title: "IsConnecting", file: "3-client-isConnecting", order: 28 },
|
||||
{ title: "JoinTournament", file: "3-client-joinTournament", order: 29 },
|
||||
{ title: "LoadImage", file: "3-client-loadImage", order: 30 },
|
||||
{ title: "LoadSong", file: "3-client-loadSong", order: 31 },
|
||||
{ title: "On", file: "3-client-on", order: 32 },
|
||||
{ title: "Once", file: "3-client-once", order: 33 },
|
||||
{ title: "PlaySong", file: "3-client-playSong", order: 34 },
|
||||
{ title: "RemoveAuthorizedUser", file: "3-client-removeAuthorizedUser", order: 35 },
|
||||
{ title: "RemoveListener", file: "3-client-removeListener", order: 36 },
|
||||
{ title: "RemoveQualifierMap", file: "3-client-removeQualifierMap", order: 37 },
|
||||
{ title: "RemoveTournamentPool", file: "3-client-removeTournamentPool", order: 38 },
|
||||
{ title: "RemoveTournamentPoolMap", file: "3-client-removeTournamentPoolMap", order: 39 },
|
||||
{ title: "RemoveTournamentTeam", file: "3-client-removeTournamentTeam", order: 40 },
|
||||
{ title: "RemoveUserFromMatch", file: "3-client-removeUserFromMatch", order: 41 },
|
||||
{ title: "ReturnToMenu", file: "3-client-returnToMenu", order: 42 },
|
||||
{ title: "RevokeBotToken", file: "3-client-revokeBotToken", order: 43 },
|
||||
{ title: "SendResponse", file: "3-client-sendResponse", order: 44 },
|
||||
{ title: "SetAuthToken", file: "3-client-setAuthToken", order: 45 },
|
||||
{ title: "SetMatchLeader", file: "3-client-setMatchLeader", order: 46 },
|
||||
{ title: "SetMatchMap", file: "3-client-setMatchMap", order: 47 },
|
||||
{ title: "SetQualifierFlags", file: "3-client-setQualifierFlags", order: 48 },
|
||||
{ title: "SetQualifierImage", file: "3-client-setQualifierImage", order: 49 },
|
||||
{ title: "SetQualifierInfoChannel", file: "3-client-setQualifierInfoChannel", order: 50 },
|
||||
{ title: "SetQualifierLeaderboardSort", file: "3-client-setQualifierLeaderboardSort", order: 51 },
|
||||
{ title: "SetQualifierName", file: "3-client-setQualifierName", order: 52 },
|
||||
{ title: "SetTournamentAllowUnauthorizedView", file: "3-client-setTournamentAllowUnauthorizedView", order: 53 },
|
||||
{ title: "SetTournamentBannedMods", file: "3-client-setTournamentBannedMods", order: 54 },
|
||||
{ title: "SetTournamentEnablePools", file: "3-client-setTournamentEnablePools", order: 55 },
|
||||
{ title: "SetTournamentEnableTeams", file: "3-client-setTournamentEnableTeams", order: 56 },
|
||||
{ title: "SetTournamentImage", file: "3-client-setTournamentImage", order: 57 },
|
||||
{ title: "SetTournamentName", file: "3-client-setTournamentName", order: 58 },
|
||||
{ title: "SetTournamentPoolName", file: "3-client-setTournamentPoolName", order: 59 },
|
||||
{ title: "SetTournamentScoreUpdateFrequency", file: "3-client-setTournamentScoreUpdateFrequency", order: 60 },
|
||||
{ title: "SetTournamentShowQualifierButton", file: "3-client-setTournamentShowQualifierButton", order: 61 },
|
||||
{ title: "SetTournamentShowTournamentButton", file: "3-client-setTournamentShowTournamentButton", order: 62 },
|
||||
{ title: "SetTournamentTeamImage", file: "3-client-setTournamentTeamImage", order: 63 },
|
||||
{ title: "SetTournamentTeamName", file: "3-client-setTournamentTeamName", order: 64 },
|
||||
{ title: "ShowLoadedImage", file: "3-client-showLoadedImage", order: 65 },
|
||||
{ title: "ShowPrompt", file: "3-client-showPrompt", order: 66 },
|
||||
{ title: "StateManager", file: "3-client-stateManager", order: 67 },
|
||||
{ title: "UpdateQualifierMap", file: "3-client-updateQualifierMap", order: 68 },
|
||||
{ title: "UpdateTournamentPoolMap", file: "3-client-updateTournamentPoolMap", order: 69 },
|
||||
{ title: "UpdateUser", file: "3-client-updateUser", order: 70 }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -72,6 +139,24 @@
|
|||
{ title: "Endpoints", file: "", order: 2 },
|
||||
{ title: "Response Types", file: "", order: 3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Events",
|
||||
icon: "view_in_ar",
|
||||
items: [
|
||||
{ title: "6-", file: "", order: 1 },
|
||||
{ title: "Endpoints", file: "", order: 2 },
|
||||
{ title: "Response Types", file: "", order: 3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: "Best Practices",
|
||||
icon: "view_in_ar",
|
||||
items: [
|
||||
{ title: "7-", file: "", order: 1 },
|
||||
{ title: "Endpoints", file: "", order: 2 },
|
||||
{ title: "Response Types", file: "", order: 3 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -214,6 +299,55 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Get current document index and navigation info
|
||||
function getCurrentDocInfo(currentDocData: any) {
|
||||
if (!currentDocData) return null;
|
||||
|
||||
const currentIndex = allDocs.findIndex(doc => doc.file === currentDoc);
|
||||
return {
|
||||
current: currentDocData,
|
||||
index: currentIndex,
|
||||
total: allDocs.length
|
||||
};
|
||||
}
|
||||
|
||||
// Navigate to previous document
|
||||
function navigatePrevious() {
|
||||
const docInfo = getCurrentDocInfo(allDocs.find(doc => doc.file === currentDoc));
|
||||
if (!docInfo || docInfo.index <= 0) return;
|
||||
|
||||
const prevDoc = allDocs[docInfo.index - 1];
|
||||
selectDoc(prevDoc.category, prevDoc.file);
|
||||
}
|
||||
|
||||
// Navigate to next document
|
||||
function navigateNext() {
|
||||
const docInfo = getCurrentDocInfo(allDocs.find(doc => doc.file === currentDoc));
|
||||
if (!docInfo || docInfo.index >= docInfo.total - 1) return;
|
||||
|
||||
const nextDoc = allDocs[docInfo.index + 1];
|
||||
selectDoc(nextDoc.category, nextDoc.file);
|
||||
}
|
||||
|
||||
// Get previous and next document info for button states
|
||||
function getNavigationState(currentDocData: any) {
|
||||
const docInfo = getCurrentDocInfo(currentDocData);
|
||||
if (!docInfo) return { hasPrev: false, hasNext: false, prev: null, next: null };
|
||||
|
||||
const hasPrev = docInfo.index > 0;
|
||||
const hasNext = docInfo.index < docInfo.total - 1;
|
||||
|
||||
return {
|
||||
hasPrev,
|
||||
hasNext,
|
||||
prev: hasPrev ? allDocs[docInfo.index - 1] : null,
|
||||
next: hasNext ? allDocs[docInfo.index + 1] : null
|
||||
};
|
||||
}
|
||||
|
||||
// Reactive statement to get navigation state
|
||||
$: navigationState = getNavigationState(allDocs.find(doc => doc.file === currentDoc));
|
||||
|
||||
onMount(() => {
|
||||
// Initialise content based on URL hash or load default
|
||||
handleHashChange();
|
||||
|
|
@ -310,6 +444,38 @@
|
|||
<div class="markdown-container" in:fly={{y: 20, duration: 300}}>
|
||||
{@html htmlContent}
|
||||
</div>
|
||||
<!-- Navigation buttons -->
|
||||
<div class="doc-navigation">
|
||||
<button
|
||||
class="nav-btn prev-btn"
|
||||
class:disabled={!navigationState.hasPrev}
|
||||
disabled={!navigationState.hasPrev}
|
||||
on:click={navigatePrevious}
|
||||
>
|
||||
<span class="material-icons">arrow_back</span>
|
||||
<div class="nav-btn-content">
|
||||
<span class="nav-btn-label">Previous</span>
|
||||
{#if navigationState.prev}
|
||||
<span class="nav-btn-title">{navigationState.prev.title}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="nav-btn next-btn"
|
||||
class:disabled={!navigationState.hasNext}
|
||||
disabled={!navigationState.hasNext}
|
||||
on:click={navigateNext}
|
||||
>
|
||||
<span class="material-icons">arrow_forward</span>
|
||||
<div class="nav-btn-content">
|
||||
<span class="nav-btn-label">Next</span>
|
||||
{#if navigationState.next}
|
||||
<span class="nav-btn-title">{navigationState.next.title}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
</div>
|
||||
|
|
@ -332,6 +498,7 @@
|
|||
overflow-y: auto;
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 5;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
|
|
@ -358,6 +525,7 @@
|
|||
|
||||
.sidebar-nav {
|
||||
padding: 1rem 0;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.category {
|
||||
|
|
@ -520,6 +688,103 @@
|
|||
box-shadow: 0 0 12px var(--accent-glow);
|
||||
}
|
||||
|
||||
.doc-navigation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 2rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.5rem;
|
||||
background-color: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-family: inherit;
|
||||
min-height: 4rem;
|
||||
flex: 1;
|
||||
max-width: auto;
|
||||
}
|
||||
|
||||
.nav-btn:hover:not(.disabled) {
|
||||
background-color: var(--accent-color);
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 12px var(--accent-glow);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background-color: var(--bg-tertiary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
.nav-btn-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.next-btn .nav-btn-content {
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.nav-btn-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.nav-btn-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.prev-btn {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
margin-left: auto;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.doc-navigation {
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
max-width: none;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-btn-content {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.next-btn .nav-btn-content {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Markdown content styling */
|
||||
.markdown-container {
|
||||
max-width: 800px;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
import EditMap from "$lib/components/popups/EditMap.svelte";
|
||||
import Popup from "$lib/components/notifications/Popup.svelte";
|
||||
import PreviouslyPlayedSongs from "$lib/components/modules/PreviouslyPlayedSongs.svelte";
|
||||
import { xml } from "d3";
|
||||
|
||||
export let data;
|
||||
|
||||
|
|
@ -305,7 +304,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleMatchDeleted(params: any) {
|
||||
function handleMatchDeleted(params: [Match, Tournament]) {
|
||||
console.log("Match deleted:", params);
|
||||
if (params[0].guid === matchGuid) {
|
||||
goto(`/tournaments/${tournamentGuid}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue