Add many interactive components

- Version selector for MidnightLib is now finally working
- Added buttons to copy code from code blocks
- Added color chooser in Puzzle wiki
This commit is contained in:
Martin Prokoph
2024-09-18 01:19:42 +02:00
parent fc8286964f
commit 0c18baf478
5 changed files with 189 additions and 140 deletions

View File

@@ -0,0 +1,71 @@
---
interface Props {
includeHashtag?: boolean
}
const { includeHashtag } = Astro.props
import { Icon } from 'astro-icon/components'
---
<section class="my-6">
<div class="">
<p class="text-lg">Select a color below to get the hex code:</p>
<div class="flex">
<fieldset>
<input
type="color"
id="color-select"
class="h-16 w-48 hover:cursor-pointer rounded-xl border-neutral-900 dark:border-neutral-100 border-4"
/>
</fieldset>
<button
id="copy-button"
class="button has-icon color-secondary ml-3 rounded-md text-white hover:cursor-pointer appearance-none h-16">
<Icon id="copy-icon" name="ion:copy-outline" />
<Icon id="success-icon" name="ion:checkmark-outline" class="hidden" />
<p id="color-label">{includeHashtag ? '#' : ''}000000</p>
</div>
</div>
</section>
<script>
// Get the 2 elements from above
const colorLabel = document.getElementById("color-label");
const colorSelect = document.getElementById("color-select");
const copyButton = document.getElementById("copy-button");
const copyIcon = document.getElementById("copy-icon");
const successIcon = document.getElementById("success-icon");
colorSelect?.addEventListener("change", async (e) => {
const {
target: { value }, // Returns the chosen color (already in hex format :D)
} = e;
if (colorLabel) colorLabel.innerText = colorLabel.innerText.startsWith('#') ? value : value.replace('#', '');
});
// Add event listener to copy the color to the clipboard
if (colorLabel) copyButton?.addEventListener('click', () => copyString(colorLabel.innerText, copyButton));
async function copyString(text: string, button: HTMLElement) {
await navigator.clipboard.writeText(text);
// visual feedback that task is completed
if (button) {
button.style.backgroundColor = "#"+text.replace('#', '');
button.style.borderColor = "#"+text.replace('#', '');
}
if (copyIcon) copyIcon.style.display = 'none';
if (successIcon) successIcon.style.display = 'initial';
setTimeout(() => {
if (button) {
button.style.backgroundColor = 'var(--secondary-100)';
button.style.borderColor = 'var(--secondary-100)';
}
if (copyIcon) copyIcon.style.display = 'initial';
if (successIcon) successIcon.style.display = 'none';
}, 1000);
}
</script>

View File

@@ -1,161 +1,68 @@
--- ---
import { loaderList, versionList, selectedLoader, selectedVersion, getResultingVersion } from '../js/modversion.js' import { Icon } from 'astro-icon/components'
import { loaderList, versionList} from '../js/modversion.js'
--- ---
<div id="version-dropdown"> <div id="version-dropdown">
<div class="container"> <div class="">
<div class="wrapper"> <div class="wrapper">
<label for="loader-selector" class="sr-only">Select the Modloader</label> <label for="loader-selector" class="sr-only">Select the Modloader</label>
<select <select
x-data="{
loader: localStorage.selectedLoader || 'fabric',
switchLoader: function (newValue) {
localStorage.selectedLoader = newValue;
console.log('New loader: ' + localStorage.selectedLoader);
},
}"
name="loader-selector" name="loader-selector"
id="loader-selector" id="loader-selector"
class="appearance-none cursor-pointer rounded-md pl-3 pr-2 py-1.5 dark:bg-stone-950 dark:text-white focus-visible:outline-none" class="selector cursor-pointer rounded-md pl-3 pr-2 py-1.5 border-2 dark:border-green-300 dark:bg-neutral-800 dark:text-white focus-visible:outline-none"
x-model="loader" aria-label="Choose the Modloader">
aria-label="Choose the Modloader"
@change="switchLoader($event.target.value)">
{ loaderList.map((loader) => { loaderList.map((loader) =>
<option value={loader}>{loader.charAt(0).toUpperCase() + loader.slice(1)}</option> <option value={loader}>{loader.charAt(0).toUpperCase() + loader.replace("neoforge", "NeoForge").slice(1)}</option>
)} )}
</select> </select>
<label for="version-selector" class="sr-only">Select the Version</label> <label for="version-selector" class="sr-only">Select the Version</label>
<select <select
x-data="{
version: localStorage.selectedVersion || '1.21.1',
switchVersion: function (newValue) {
localStorage.selectedVersion = newValue;
console.log('New version: ' + localStorage.selectedVersion);
console.log(getResultingVersion);
},
}"
name="version-selector" name="version-selector"
id="version-selector" id="version-selector"
class="appearance-none cursor-pointer rounded-md pl-3 pr-2 py-1.5 dark:bg-stone-950 dark:text-white focus-visible:outline-none" class="selector cursor-pointer rounded-md ml-2 pl-3 pr-2 py-1.5 border-2 dark:border-green-300 dark:bg-neutral-800 dark:text-white focus-visible:outline-none"
x-model="version" aria-label="Choose the Version">
aria-label="Choose the Version"
@change="switchVersion($event.target.value)">
{ versionList.map((version) => { versionList.map((version) =>
<option value={version}>{version}</option> <option value={version}>{version}</option>
)} )}
<Icon name="ion:copy-outline"></Icon>
</select> </select>
<label for="version-selector" x-text="">{}</label>
</div> </div>
</div> </div>
</div> </div>
<script>
import { getResultingVersion, setGameVersion, setLoaderVersion } from "../js/modversion";
// Get the gradle.properties code block
const versionLabel = document.getElementById("midnightlib-version");
// Get the 2 selectors defined above
const loaderSelector = document.getElementById("loader-selector");
const versionSelector = document.getElementById("version-selector");
loaderSelector?.addEventListener("change", async (e) => {
const {
target: { value }, // Returns the current loader string
} = e;
setLoaderVersion(value);
if (versionLabel) versionLabel.innerText = `midnightlib_version = `+getResultingVersion();
});
versionSelector?.addEventListener("change", async (e) => {
const {
target: { value }, // Returns the current version string
} = e;
setGameVersion(value);
if (versionLabel) versionLabel.innerText = `midnightlib_version = `+getResultingVersion();
});
</script>
<style lang="scss" is:global> <style lang="scss" is:global>
@use '../assets/scss/base/breakpoint' as *; #loader-selector::after {
@use '../assets/scss/base/outline' as *; content: "v";
}
#version-dropdown { .selector:is(.darkmode *) {
> .container { background-color: var(--dark-100);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.wrapper {
display: flex;
align-items: center;
gap: 1rem;
}
.wrapper {
> ul {
display: flex;
gap: 1.5rem;
list-style-type: none;
a,
button {
text-decoration: none;
font-size: 1.125rem;
line-height: 1.6875rem;
}
a:hover,
a:focus,
.is-active,
.has-version-dropdown > button:hover,
.has-version-dropdown > button:focus {
text-decoration: underline;
text-decoration-thickness: 1px;
text-decoration-style: wavy;
text-underline-offset: 7px;
}
.is-active {
font-weight: bold;
}
}
}
.has-version-dropdown {
position: relative;
> button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0;
margin-top: -1px;
border: none;
color: var(--action-color);
&:hover {
color: var(--action-color-state);
&::after {
border-color: var(--action-color-state);
}
}
&::after {
content: '';
width: 0.85rem;
height: 0.75em;
margin-top: -0.25rem;
border-style: solid;
border-width: 0.2em 0.2em 0 0;
border-color: var(--action-color);
transform: rotate(135deg);
}
&.show {
&::after {
margin-top: 0.25rem;
transform: rotate(-45deg);
}
~ ul {
display: flex;
flex-direction: column;
gap: 1rem;
}
}
}
ul {
display: none;
position: absolute;
z-index: 100;
min-width: 260px;
top: 125%;
right: 0;
bottom: auto;
left: 0;
padding: 1rem;
background-color: var(--neutral-background);
border: 2px solid black;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}
}
} }
</style> </style>

View File

@@ -3,6 +3,44 @@ import DefaultLayout from './DefaultLayout.astro'
const { frontmatter } = Astro.props const { frontmatter } = Astro.props
--- ---
<!-- Copy Code Buttons based on: https://timneubauer.dev/blog/copy-code-button-in-astro/ -->
<script>
let copyButtonLabel = "Copy Code";
let codeBlocks = Array.from(document.querySelectorAll("pre"));
for (let codeBlock of codeBlocks) {
let wrapper = document.createElement("div");
wrapper.style.position = "relative";
let copyButton = document.createElement("button");
copyButton.className = "copy-code";
copyButton.innerHTML = copyButtonLabel;
codeBlock.setAttribute("tabindex", "0");
codeBlock.appendChild(copyButton);
// wrap codebock with relative parent element
if (codeBlock.parentNode) codeBlock.parentNode.insertBefore(wrapper, codeBlock);
wrapper.appendChild(codeBlock);
copyButton.addEventListener("click", async () => {
await copyCode(codeBlock, copyButton);
});
}
async function copyCode(block, button) {
let code = block.querySelector("code");
let text = code.innerText;
await navigator.clipboard.writeText(text);
// visual feedback that task is completed
button.innerText = "Code Copied!";
setTimeout(() => {
button.innerText = copyButtonLabel;
}, 700);
}
</script>
<DefaultLayout title={frontmatter.title}> <DefaultLayout title={frontmatter.title}>
<div class="container"> <div class="container">
@@ -11,3 +49,35 @@ const { frontmatter } = Astro.props
</div> </div>
</div> </div>
</DefaultLayout> </DefaultLayout>
<style is:global>
.copy-code {
position: absolute;
top: 0;
right: 0;
background-color: var(--primary-300);
color: var(--neutral-100);
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
line-height: 1rem;
border: none;
border-top-right-radius: 0.35rem;
border-bottom-left-radius: 0.35rem;
box-shadow: -1px 1px 8px var(--primary-300);
}
.copy-code:is(.darkmode *) {
background-color: var(--secondary-100);
color: var(--dark-100);
box-shadow: -1px 1px 8px var(--secondary-100);
}
.copy-code:hover {
background-color: var(--primary-500);
box-shadow: -1px 1px 8px var(--primary-500);
}
.copy-code:hover:is(.darkmode *) {
background-color: var(--secondary-500);
box-shadow: -1px 1px 8px var(--secondary-500);
}
</style>

View File

@@ -33,14 +33,13 @@ dependencies {
``` ```
### `gradle.properties` ### `gradle.properties`
<VersionDropdown></VersionDropdown> <VersionDropdown></VersionDropdown>
{<pre><code className="language-java">midnightlib_version = {getResultingVersion()}</code></pre>} {<pre><code className="language-java" id="midnightlib-version" >midnightlib_version = {getResultingVersion()}</code></pre>}
<Notification type="info"> <Notification type="info">
<Icon name="ion:information-circle-outline" /> <Icon name="ion:information-circle-outline" />
<p> <p>
<strong>Info:</strong> You should always pick the version that suits your modloader and Minecraft version best. <strong>Info:</strong> You should always pick the version that suits your modloader and Minecraft version best.
The version selector is currently still WIP. The version selector is finally fully functional!
Find all available versions on [Modrinth](https://www.modrinth.com/mod/midnightlib/versions)
</p> </p>
</Notification> </Notification>

View File

@@ -3,6 +3,7 @@ layout: ../../layouts/MarkdownLayout.astro
title: Puzzle Wiki title: Puzzle Wiki
--- ---
import ColorPicker from '../../components/ColorPicker.astro'
import { Icon } from 'astro-icon/components' import { Icon } from 'astro-icon/components'
import { Notification } from 'accessible-astro-components' import { Notification } from 'accessible-astro-components'
@@ -51,7 +52,7 @@ screen.loading.blend=off
### Entering the world of color ### Entering the world of color
The world would be a sad place without colors. The world would be a sad place without colors.
That's why Puzzle allows you to customize them to your heart's content. That's why Puzzle allows you to customize them to your heart's content!
Custom colors can be defined in the `assets/minecraft/optifine/color.properties` file. Custom colors can be defined in the `assets/minecraft/optifine/color.properties` file.
```properties ```properties
@@ -69,11 +70,12 @@ screen.loading.bar=313244
<Notification type="default"> <Notification type="default">
<Icon name="ion:information-circle-outline" /> <Icon name="ion:information-circle-outline" />
<p> <p>
<strong>Info:</strong> Colors have to be defined using hex color codes. <strong>Info:</strong> Colors have to be defined using hex color codes (without the #).
You can use websites like <strong>[this](https://redketchup.io/color-picker)</strong> one to find nice hex color codes. You can use the color picker below to find the perfect color.
The example colors are based on the <strong>[Catppuccin Mocha](https://github.com/catppuccin/catppuccin)</strong> color pallette. The example colors are based on the <strong>[Catppuccin Mocha](https://github.com/catppuccin/catppuccin)</strong> color pallette.
</p> </p>
</Notification> </Notification>
<ColorPicker/>
<center><img alt="The Puzzle logo on a dark background with pastel colors" src="/puzzle/custom-colors.webp" width="650"></img></center> <center><img alt="The Puzzle logo on a dark background with pastel colors" src="/puzzle/custom-colors.webp" width="650"></img></center>
<p class="text-center italic">Dark mode! Finally, my eyes can rest.</p> <p class="text-center italic">Dark mode! Finally, my eyes can rest.</p>