115 lines
4.6 KiB
TypeScript
115 lines
4.6 KiB
TypeScript
import { createTextVNode, computed, defineComponent, h, inject, onServerPrefetch, ref, type VNode, watchEffect } from "vue";
|
|
import { RouterLink } from "vue-router";
|
|
import useAsyncState from "@/useAsyncState.ts";
|
|
import useHead from "@/useHead.ts";
|
|
import DJEmail from "@/DJEmail.tsx";
|
|
import getDJAPI from "@/api.ts";
|
|
|
|
export default defineComponent({
|
|
name: "ge-deutsch-article",
|
|
props: {
|
|
articleName: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
},
|
|
async setup(props) {
|
|
const currentLang = ref<"en" | "de">("de");
|
|
|
|
function clickBtn() {
|
|
currentLang.value = currentLang.value === "en" ? "de" : "en";
|
|
}
|
|
|
|
const parseDom = inject(
|
|
"dom-parse",
|
|
(innerHTML: string) => Object.assign(document.createElement("div"), { innerHTML }),
|
|
);
|
|
|
|
const { result: articleContent, stateIsReady } = useAsyncState(
|
|
"ge-deutsch-article-data",
|
|
async ({ hostUrl }) => {
|
|
const articleResponse = await fetch(`${hostUrl}/generative-energy/content/${props.articleName}.html`);
|
|
const result = await articleResponse.text();
|
|
return result;
|
|
},
|
|
);
|
|
|
|
const {
|
|
result: articleData,
|
|
stateIsReady: articleDataReady,
|
|
} = useAsyncState('article-data', ({hostUrl}) => getDJAPI(hostUrl, '/rp-articles'));
|
|
|
|
const articleMetadata = computed(() => articleData.value?.find(_ => _.slug === props.articleName));
|
|
|
|
useHead({ title: () => articleMetadata.value?.title ?? '' });
|
|
|
|
function transformArticleNode(node: Node): VNode | string {
|
|
if (node.nodeType === node.ELEMENT_NODE) {
|
|
const el = node as Element;
|
|
const attrs: Record<string, string> = {};
|
|
const children = [...node.childNodes].map((_) => transformArticleNode(_));
|
|
|
|
if (el.tagName === "P") {
|
|
(el as HTMLParagraphElement).dataset.tunit = '';
|
|
}
|
|
if ('dataset' in el && 'tunit' in (el as HTMLElement).dataset) {
|
|
if (el.tagName !== 'H1') {
|
|
el.classList.add("text-slab");
|
|
} else {
|
|
el.classList.add("title");
|
|
}
|
|
if (!('notranslate' in (el as HTMLElement).dataset)) {
|
|
children.unshift(h("button", {
|
|
class: "swap",
|
|
onClick: (e) => {
|
|
(e.target as HTMLButtonElement).parentElement?.classList.toggle("swap");
|
|
},
|
|
}, "↻"));
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < el.attributes.length; i++) {
|
|
const item = el.attributes.item(i);
|
|
if (item) {
|
|
attrs[item.name] = item.value;
|
|
}
|
|
}
|
|
|
|
return h((node as Element).tagName, attrs, children);
|
|
} else {
|
|
return createTextVNode(node.textContent ?? "");
|
|
}
|
|
}
|
|
|
|
function ArticleContentTransformed() {
|
|
if (articleContent.value) {
|
|
const dom = parseDom(articleContent.value);
|
|
return h("div", {}, [...dom.children].map((_) => transformArticleNode(_)));
|
|
}
|
|
return <div>Artikel lädt...</div>;
|
|
}
|
|
|
|
await Promise.all([stateIsReady, articleDataReady]);
|
|
|
|
return () => (
|
|
<div class="ge-article">
|
|
<div class="header">
|
|
<RouterLink to={{ name: "GEDeutsch" }}>Zur Artikelübersicht</RouterLink>
|
|
<button onClick={clickBtn}>
|
|
Sprache auf <span>{currentLang.value === "en" ? "Deutsch" : "Englisch"}</span> umschalten
|
|
</button>
|
|
</div>
|
|
<p class="text-slab">
|
|
Bei dem untenstehenden Artikel handelt es sich um eine hobbymäßige, amateurhafte Übersetzung des
|
|
Artikels „{ articleMetadata.value?.title }“ von Ray Peat. Bei Ungenauigkeiten oder Fehlübersetzungen freue ich mich über <DJEmail>eine Mail</DJEmail>!
|
|
</p>
|
|
{ articleMetadata.value?.tags?.includes('in-arbeit') && <h5 class="baustelle">🚧 Bitte beachte, dass diese Übersetzung noch in Arbeit und darum nicht fertig ist! 🚧</h5> }
|
|
<hr />
|
|
<article class={`lang-${currentLang.value}`}>
|
|
<ArticleContentTransformed />
|
|
</article>
|
|
</div>
|
|
);
|
|
},
|
|
});
|