Make tooltips render automatically
This commit is contained in:
@@ -16,6 +16,7 @@ export default defineNuxtConfig({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
toc: {
|
toc: {
|
||||||
|
// @ts-ignore
|
||||||
title: "Table of Contents",
|
title: "Table of Contents",
|
||||||
depth: 4, // include h4 headings
|
depth: 4, // include h4 headings
|
||||||
searchDepth: 2
|
searchDepth: 2
|
||||||
@@ -51,6 +52,9 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
srcDir: "src/",
|
srcDir: "src/",
|
||||||
ssr: false,
|
ssr: false,
|
||||||
|
typescript: {
|
||||||
|
typeCheck: true
|
||||||
|
},
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [
|
plugins: [
|
||||||
ViteComponents({
|
ViteComponents({
|
||||||
|
|||||||
Generated
+19
-1
@@ -30,7 +30,8 @@
|
|||||||
"nuxt": "^3.14.1592",
|
"nuxt": "^3.14.1592",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"unplugin-icons": "^22.1.0",
|
"unplugin-icons": "^22.1.0",
|
||||||
"unplugin-vue-components": "^28.4.1"
|
"unplugin-vue-components": "^28.4.1",
|
||||||
|
"vue-tsc": "^2.2.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -12502,6 +12503,23 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-tsc": {
|
||||||
|
"version": "2.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.8.tgz",
|
||||||
|
"integrity": "sha512-jBYKBNFADTN+L+MdesNX/TB3XuDSyaWynKMDgR+yCSln0GQ9Tfb7JS2lr46s2LiFUT1WsmfWsSvIElyxzOPqcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/typescript": "~2.4.11",
|
||||||
|
"@vue/language-core": "2.2.8"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vue-tsc": "bin/vue-tsc.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/web-namespaces": {
|
"node_modules/web-namespaces": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|||||||
+2
-1
@@ -33,7 +33,8 @@
|
|||||||
"nuxt": "^3.14.1592",
|
"nuxt": "^3.14.1592",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"unplugin-icons": "^22.1.0",
|
"unplugin-icons": "^22.1.0",
|
||||||
"unplugin-vue-components": "^28.4.1"
|
"unplugin-vue-components": "^28.4.1",
|
||||||
|
"vue-tsc": "^2.2.8"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"@parcel/watcher"
|
"@parcel/watcher"
|
||||||
|
|||||||
+5
-8
@@ -6,7 +6,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const bootstrap = useNuxtApp().$bootstrap;
|
import { useStateStore } from "@/stores/stateStore";
|
||||||
|
|
||||||
|
const store = useStateStore();
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
titleTemplate: (titleChunk: string | undefined): string | null => {
|
titleTemplate: (titleChunk: string | undefined): string | null => {
|
||||||
@@ -16,12 +18,7 @@ useHead({
|
|||||||
|
|
||||||
// Access bootstrap after the DOM is ready
|
// Access bootstrap after the DOM is ready
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Initialize popovers
|
// @ts-ignore
|
||||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
|
store.initBootstrap();
|
||||||
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
|
|
||||||
|
|
||||||
// Initialize tooltips
|
|
||||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
|
||||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<span ref="wrapper">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useStateStore } from "@/stores/stateStore";
|
||||||
|
|
||||||
|
const store = useStateStore();
|
||||||
|
const wrapper = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
// Initialize Bootstrap hoverables after the DOM is ready
|
||||||
|
onMounted(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
store.stickyBootstrap(wrapper?.value?.firstElementChild);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useStateStore } from "@/stores/stateStore";
|
||||||
|
|
||||||
|
const store = useStateStore();
|
||||||
|
|
||||||
|
// Initialize Bootstrap hoverables after the DOM is ready
|
||||||
|
onMounted(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
store.initBootstrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Or its updated
|
||||||
|
onUpdated(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
store.initBootstrap();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<a href="#">Overview</a>
|
<a href="#">Overview</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ArticlesTableOfContentsLink :links="toc.links" :first="true" class="font-smaller mb-0" />
|
<ArticlesTableOfContentsLink :links="toc.links" :first="true" class="font-smaller" />
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul>
|
<ul class="mb-0">
|
||||||
<template v-for="link in links" :key="link.id">
|
<template v-for="link in links" :key="link.id">
|
||||||
<li>
|
<li>
|
||||||
<a :href="'#' + link.id">{{ link.text }}</a>
|
<a :href="'#' + link.id">{{ link.text }}</a>
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
<div @click="copyCode" class="position-absolute" style="top: 10px; right: 10px;">
|
<div @click="copyCode" class="position-absolute" style="top: 10px; right: 10px;">
|
||||||
<template v-if="copied">
|
<template v-if="!copied">
|
||||||
<code>Copied!</code>
|
<BootstrapHoverable>
|
||||||
|
<button class="copy text-secondary" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Copy to clipboard">
|
||||||
|
<IFaClone />
|
||||||
|
</button>
|
||||||
|
</BootstrapHoverable>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<BootstrapClickable>
|
||||||
|
<button class="copy text-secondary" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Copied!">
|
||||||
|
<IFaCheck class="text-success" />
|
||||||
|
</button>
|
||||||
|
</BootstrapClickable>
|
||||||
</template>
|
</template>
|
||||||
<button class="copy text-secondary" title="Copy code">
|
|
||||||
<IFaClone />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<pre :class="$props.class"><code class="language-{{ language }}"><slot /></code></pre>
|
<pre :class="$props.class"><code class="language-{{ language }}"><slot /></code></pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,7 +73,7 @@ const copyCode = async (e: Event) => {
|
|||||||
try {
|
try {
|
||||||
const target = e.currentTarget as HTMLElement;
|
const target = e.currentTarget as HTMLElement;
|
||||||
const element = target.nextElementSibling as HTMLPreElement | null;
|
const element = target.nextElementSibling as HTMLPreElement | null;
|
||||||
const textToCopy = element ? element.textContent.trim() : "";
|
const textToCopy = element ? element.textContent!.trim() : "";
|
||||||
|
|
||||||
await navigator.clipboard.writeText(textToCopy);
|
await navigator.clipboard.writeText(textToCopy);
|
||||||
copied.value = true;
|
copied.value = true;
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ContentCollectionItem } from "@nuxt/content";
|
import type { ContentCollectionItem } from "@nuxt/content";
|
||||||
|
import { useStateStore } from "@/stores/stateStore";
|
||||||
|
|
||||||
const { params } = useRoute();
|
const { params } = useRoute();
|
||||||
|
|
||||||
@@ -70,6 +71,12 @@ const { data: article } = await useAsyncData<ContentCollectionItem | null>(
|
|||||||
() => queryCollection("content").path("/articles/" + params.slug).first()
|
() => queryCollection("content").path("/articles/" + params.slug).first()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const store = useStateStore();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
store.initBootstrap();
|
||||||
|
});
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: article.value?.title,
|
title: article.value?.title,
|
||||||
description: article.value?.description
|
description: article.value?.description
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
// @ts-ignore
|
||||||
import bootstrap from "bootstrap/dist/js/bootstrap.bundle.min";
|
import bootstrap from "bootstrap/dist/js/bootstrap.bundle.min";
|
||||||
|
|
||||||
export default defineNuxtPlugin(nuxtApp => {
|
export default defineNuxtPlugin(nuxtApp => {
|
||||||
nuxtApp.provide("bootstrap", bootstrap);
|
nuxtApp.provide("bootstrap", bootstrap);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { defineStore } from "pinia"
|
import { defineStore } from "pinia"
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import bootstrap from "bootstrap/dist/js/bootstrap.bundle.min";
|
||||||
|
|
||||||
export const useStateStore = defineStore("state", () => {
|
export const useStateStore = defineStore("state", () => {
|
||||||
|
|
||||||
const colorMode = ref<string>("light");
|
const colorMode = ref<string>("light");
|
||||||
@@ -10,7 +13,28 @@ export const useStateStore = defineStore("state", () => {
|
|||||||
html.setAttribute('data-bs-theme', colorMode.value);
|
html.setAttribute('data-bs-theme', colorMode.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { colorMode, toggleColorMode }
|
const popoverList = ref<any[]>([]);
|
||||||
|
const tooltipList = ref<any[]>([]);
|
||||||
|
|
||||||
|
const initBootstrap = (): void => {
|
||||||
|
// Initialize popovers
|
||||||
|
popoverList.value.forEach(popover => popover.dispose());
|
||||||
|
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
|
||||||
|
popoverList.value = [...popoverTriggerList].map(popover => new bootstrap.Popover(popover));
|
||||||
|
|
||||||
|
// Initialize tooltips
|
||||||
|
tooltipList.value.forEach(tooltip => tooltip.dispose());
|
||||||
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||||
|
tooltipList.value = [...tooltipTriggerList].map(tooltip => new bootstrap.Tooltip(tooltip, { delay: { show: 10, hide: 0 } }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const stickyBootstrap = (tooltip: Element | null | undefined): void => {
|
||||||
|
tooltipList.value.forEach(tooltip => tooltip.dispose());
|
||||||
|
tooltipList.value = [new bootstrap.Tooltip(tooltip, { trigger: "manual" })];
|
||||||
|
tooltipList.value[0].show();
|
||||||
|
};
|
||||||
|
|
||||||
|
return { colorMode, toggleColorMode, initBootstrap, stickyBootstrap }
|
||||||
}, {
|
}, {
|
||||||
persist: process.env.NODE_ENV === 'development' ? true : false,
|
persist: process.env.NODE_ENV === 'development' ? true : false,
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-7
@@ -1,10 +1,4 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"extends": "./.nuxt/tsconfig.json",
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
"compilerOptions": {
|
|
||||||
"types": [
|
|
||||||
"bootstrap",
|
|
||||||
"unplugin-icons/types/vue"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user