fix: remove inline onclick
This commit is contained in:
@@ -1,5 +1,28 @@
|
|||||||
import { parseClassList } from './utils';
|
import { parseClassList } from './utils';
|
||||||
|
|
||||||
|
const initSynopsisToggle = (): void => {
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
const target = e.target;
|
||||||
|
if (!(target instanceof Element)) return;
|
||||||
|
|
||||||
|
const btn = target.closest<HTMLButtonElement>('[data-synopsis-toggle]');
|
||||||
|
if (!btn) return;
|
||||||
|
const container = document.getElementById('synopsis-container');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const isClamped = container.classList.contains('line-clamp-6');
|
||||||
|
if (isClamped) {
|
||||||
|
container.classList.remove('line-clamp-6');
|
||||||
|
btn.textContent = 'Show less';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.classList.add('line-clamp-6');
|
||||||
|
btn.textContent = 'Read more';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
initSynopsisToggle();
|
||||||
|
|
||||||
const setDropdownMenuState = (menu: HTMLElement, isOpen: boolean): void => {
|
const setDropdownMenuState = (menu: HTMLElement, isOpen: boolean): void => {
|
||||||
// data attributes store the class lists to add/remove
|
// data attributes store the class lists to add/remove
|
||||||
const openClasses = parseClassList(menu.getAttribute('data-dropdown-open-classes'));
|
const openClasses = parseClassList(menu.getAttribute('data-dropdown-open-classes'));
|
||||||
|
|||||||
@@ -41,3 +41,56 @@ const initDiscoverTabs = (): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
initDiscoverTabs();
|
initDiscoverTabs();
|
||||||
|
|
||||||
|
const initSurpriseMe = (): void => {
|
||||||
|
let isFetchingRandom = false;
|
||||||
|
|
||||||
|
const onClick = async (): Promise<void> => {
|
||||||
|
if (isFetchingRandom) return;
|
||||||
|
|
||||||
|
const btn = document.getElementById('surprise-btn') as HTMLButtonElement | null;
|
||||||
|
if (!btn) return;
|
||||||
|
isFetchingRandom = true;
|
||||||
|
|
||||||
|
const spinner = document.getElementById('surprise-spinner');
|
||||||
|
const text = document.getElementById('surprise-text');
|
||||||
|
const icon = document.getElementById('surprise-icon');
|
||||||
|
|
||||||
|
btn.disabled = true;
|
||||||
|
spinner?.classList.remove('hidden');
|
||||||
|
icon?.classList.add('hidden');
|
||||||
|
if (text) text.textContent = 'Picking…';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/jikan/random/anime?t=${Date.now()}`, { cache: 'no-store' });
|
||||||
|
if (!res.ok) throw new Error('Failed to fetch random anime');
|
||||||
|
const json = (await res.json()) as unknown;
|
||||||
|
const data = (json as { data?: unknown }).data as { mal_id?: unknown } | undefined;
|
||||||
|
const malId = typeof data?.mal_id === 'number' ? data.mal_id : 0;
|
||||||
|
if (malId > 0) {
|
||||||
|
window.location.href = `/anime/${malId}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error('Random anime missing mal_id');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
alert('Could not pick a random anime right now. Please try again.');
|
||||||
|
} finally {
|
||||||
|
isFetchingRandom = false;
|
||||||
|
btn.disabled = false;
|
||||||
|
spinner?.classList.add('hidden');
|
||||||
|
icon?.classList.remove('hidden');
|
||||||
|
if (text) text.textContent = 'Surprise Me';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
const target = e.target;
|
||||||
|
if (!(target instanceof Element)) return;
|
||||||
|
const surprise = target.closest('[data-surprise-me]');
|
||||||
|
if (!surprise) return;
|
||||||
|
void onClick();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
initSurpriseMe();
|
||||||
|
|||||||
@@ -64,3 +64,25 @@ class UIDropdown extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('ui-dropdown', UIDropdown);
|
customElements.define('ui-dropdown', UIDropdown);
|
||||||
|
|
||||||
|
const initStudioDropdown = (): void => {
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
const target = e.target;
|
||||||
|
if (!(target instanceof Element)) return;
|
||||||
|
|
||||||
|
const btn = target.closest<HTMLButtonElement>('button[data-studio-select]');
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
const input = document.getElementById('studio-input') as HTMLInputElement | null;
|
||||||
|
const form = document.getElementById('browse-search-form') as HTMLFormElement | null;
|
||||||
|
if (!input || !form) return;
|
||||||
|
|
||||||
|
input.value = btn.dataset.studioSelect ?? '';
|
||||||
|
form.requestSubmit();
|
||||||
|
|
||||||
|
const dropdown = btn.closest('ui-dropdown') as { close?: () => void } | null;
|
||||||
|
dropdown?.close?.();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
initStudioDropdown();
|
||||||
|
|||||||
@@ -147,17 +147,7 @@
|
|||||||
<h2 class="mb-4 mt-2 text-base font-normal text-foreground">Synopsis</h2>
|
<h2 class="mb-4 mt-2 text-base font-normal text-foreground">Synopsis</h2>
|
||||||
<p id="synopsis-container" class="text-foreground-muted text-base leading-relaxed line-clamp-6 md:line-clamp-none whitespace-pre-line">{{if $anime.Synopsis}}{{$anime.Synopsis}}{{else}}No synopsis available.{{end}}</p>
|
<p id="synopsis-container" class="text-foreground-muted text-base leading-relaxed line-clamp-6 md:line-clamp-none whitespace-pre-line">{{if $anime.Synopsis}}{{$anime.Synopsis}}{{else}}No synopsis available.{{end}}</p>
|
||||||
{{if and $anime.Synopsis (gt (len $anime.Synopsis) 400)}}
|
{{if and $anime.Synopsis (gt (len $anime.Synopsis) 400)}}
|
||||||
<button id="synopsis-toggle" class="mt-2 text-sm font-normal text-foreground-muted transition-colors hover:text-foreground md:hidden" onclick="
|
<button id="synopsis-toggle" data-synopsis-toggle class="mt-2 text-sm font-normal text-foreground-muted transition-colors hover:text-foreground md:hidden">
|
||||||
const container = document.getElementById('synopsis-container');
|
|
||||||
const btn = document.getElementById('synopsis-toggle');
|
|
||||||
if (container.classList.contains('line-clamp-6')) {
|
|
||||||
container.classList.remove('line-clamp-6');
|
|
||||||
btn.textContent = 'Show less';
|
|
||||||
} else {
|
|
||||||
container.classList.add('line-clamp-6');
|
|
||||||
btn.textContent = 'Read more';
|
|
||||||
}
|
|
||||||
">
|
|
||||||
Read more
|
Read more
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -47,12 +47,12 @@
|
|||||||
|
|
||||||
{{define "studio_dropdown_items"}}
|
{{define "studio_dropdown_items"}}
|
||||||
{{if eq .Page 1}}
|
{{if eq .Page 1}}
|
||||||
<button type="button" class="flex w-full items-center px-5 py-2.5 text-left transition-colors hover:bg-surface-hover text-sm text-foreground" onclick="(function(){const input=document.getElementById('studio-input'); if(input){input.value=''}; const form=document.getElementById('browse-search-form'); if(form){form.requestSubmit()}; const dd=document.getElementById('studio-dropdown'); dd?.close?.()})()">
|
<button type="button" data-studio-select="" class="flex w-full items-center px-5 py-2.5 text-left transition-colors hover:bg-surface-hover text-sm text-foreground">
|
||||||
Any Studio
|
Any Studio
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{range .StudioItems}}
|
{{range .StudioItems}}
|
||||||
<button type="button" class="flex w-full items-center px-5 py-2.5 text-left transition-colors hover:bg-surface-hover text-sm text-foreground" onclick="(function(){const input=document.getElementById('studio-input'); if(input){input.value='{{.ID}}'}; const form=document.getElementById('browse-search-form'); if(form){form.requestSubmit()}; const dd=document.getElementById('studio-dropdown'); dd?.close?.()})()">
|
<button type="button" data-studio-select="{{.ID}}" class="flex w-full items-center px-5 py-2.5 text-left transition-colors hover:bg-surface-hover text-sm text-foreground">
|
||||||
{{.Name}}
|
{{.Name}}
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,43 +1,5 @@
|
|||||||
{{define "title"}}Discover{{end}}
|
{{define "title"}}Discover{{end}}
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<script>
|
|
||||||
let isFetchingRandom = false;
|
|
||||||
|
|
||||||
async function handleSurpriseMe() {
|
|
||||||
if (isFetchingRandom) return;
|
|
||||||
isFetchingRandom = true;
|
|
||||||
const btn = document.getElementById('surprise-btn');
|
|
||||||
const spinner = document.getElementById('surprise-spinner');
|
|
||||||
const text = document.getElementById('surprise-text');
|
|
||||||
const icon = document.getElementById('surprise-icon');
|
|
||||||
btn?.setAttribute('disabled', 'true');
|
|
||||||
spinner?.classList.remove('hidden');
|
|
||||||
icon?.classList.add('hidden');
|
|
||||||
if (text) text.textContent = 'Picking…';
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/api/jikan/random/anime?t=${Date.now()}`, {
|
|
||||||
cache: 'no-store'
|
|
||||||
});
|
|
||||||
if (!res.ok) throw new Error('Failed to fetch random anime');
|
|
||||||
const json = await res.json();
|
|
||||||
if (json.data && typeof json.data.mal_id === 'number' && json.data.mal_id > 0) {
|
|
||||||
window.location.href = `/anime/${json.data.mal_id}`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error('Random anime missing mal_id');
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
alert('Could not pick a random anime right now. Please try again.');
|
|
||||||
} finally {
|
|
||||||
isFetchingRandom = false;
|
|
||||||
btn?.removeAttribute('disabled');
|
|
||||||
spinner?.classList.add('hidden');
|
|
||||||
icon?.classList.remove('hidden');
|
|
||||||
if (text) text.textContent = 'Surprise Me';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-12 pb-12">
|
<div class="flex flex-col gap-12 pb-12">
|
||||||
<section class="flex flex-col items-center justify-center bg-background-surface px-6 py-16 text-center">
|
<section class="flex flex-col items-center justify-center bg-background-surface px-6 py-16 text-center">
|
||||||
<div class="flex max-w-2xl flex-col items-center gap-5">
|
<div class="flex max-w-2xl flex-col items-center gap-5">
|
||||||
@@ -49,8 +11,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
id="surprise-btn"
|
id="surprise-btn"
|
||||||
|
data-surprise-me
|
||||||
class="group relative inline-flex h-11 items-center justify-center gap-2 bg-background-button px-5 text-base font-normal text-foreground transition-colors hover:bg-background-button-hover active:bg-background-button-hover disabled:opacity-70 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent sm:h-12"
|
class="group relative inline-flex h-11 items-center justify-center gap-2 bg-background-button px-5 text-base font-normal text-foreground transition-colors hover:bg-background-button-hover active:bg-background-button-hover disabled:opacity-70 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent sm:h-12"
|
||||||
onclick="handleSurpriseMe()"
|
|
||||||
>
|
>
|
||||||
<span id="surprise-spinner" class="hidden h-5 w-5 animate-spin rounded-full border-2 border-background border-t-transparent"></span>
|
<span id="surprise-spinner" class="hidden h-5 w-5 animate-spin rounded-full border-2 border-background border-t-transparent"></span>
|
||||||
<svg id="surprise-icon" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
<svg id="surprise-icon" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|||||||
Reference in New Issue
Block a user