Fix Canvas initial position and add tags popover
This commit is contained in:
parent
a55b98e07a
commit
a228b7d222
7
app.vue
7
app.vue
|
|
@ -1,8 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { data: tags } = await useAsyncData('tags', queryContent('/tags').findOne);
|
||||||
|
|
||||||
|
provide('tags', tags);
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="published-container print is-readable-line-width has-navigation has-graph has-outline" style="">
|
<div class="published-container print is-readable-line-width has-navigation has-graph has-outline" style="">
|
||||||
<div class="site-body">
|
<div class="site-body">
|
||||||
<LeftComponent />
|
<LeftComponent />
|
||||||
<NuxtLoadingIndicator />
|
|
||||||
<MainComponent />
|
<MainComponent />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1008,6 +1008,22 @@ html {
|
||||||
--font-mermaid: var(--font-text);
|
--font-mermaid: var(--font-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preload {
|
||||||
|
padding: 20px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: break-word
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from {
|
||||||
|
transform: rotate(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
audio {
|
audio {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
@ -2778,7 +2794,7 @@ body:not(.native-scrollbars) * {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: var(--layer-popover);
|
z-index: var(--layer-popover);
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
min-height: 60px;
|
min-height: 30px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -2835,6 +2851,18 @@ body:not(.native-scrollbars) * {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popover.hover-popover>.tag-embed {
|
||||||
|
min-height: initial;
|
||||||
|
height: initial;
|
||||||
|
max-height: initial;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover.hover-popover>.tag-embed {
|
||||||
|
min-height: initial;
|
||||||
|
height: initial;
|
||||||
|
}
|
||||||
|
|
||||||
.popover.hover-popover>.canvas-embed {
|
.popover.hover-popover>.canvas-embed {
|
||||||
min-height: 350px;
|
min-height: 350px;
|
||||||
}
|
}
|
||||||
|
|
@ -2848,6 +2876,15 @@ body:not(.native-scrollbars) * {
|
||||||
padding: var(--file-margins);
|
padding: var(--file-margins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popover.hover-popover>.tag-embed>.markdown-embed-content>.markdown-preview-view {
|
||||||
|
padding: var(--size-4-3) var(--size-4-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover.hover-popover>.tag-embed p {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.popover.hover-popover>.markdown-embed .mod-header+div>*:first-child {
|
.popover.hover-popover>.markdown-embed .mod-header+div>*:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,29 +11,40 @@ const props = defineProps<Props>();
|
||||||
|
|
||||||
let dragging = false, posX = 0, posY = 0, dispX = ref(0), dispY = ref(0), minZoom = ref(0.3), zoom = ref(1);
|
let dragging = false, posX = 0, posY = 0, dispX = ref(0), dispY = ref(0), minZoom = ref(0.3), zoom = ref(1);
|
||||||
let centerX = ref(0), centerY = ref(0), canvas = ref<HTMLDivElement>();
|
let centerX = ref(0), centerY = ref(0), canvas = ref<HTMLDivElement>();
|
||||||
|
let minX = ref(+Infinity), minY = ref(+Infinity), maxX = ref(-Infinity), maxY = ref(-Infinity);
|
||||||
|
let bbox = ref<DOMRect>();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
let minX = +Infinity, minY = +Infinity, maxX = -Infinity, maxY = -Infinity;
|
let _minX = +Infinity, _minY = +Infinity, _maxX = -Infinity, _maxY = -Infinity;
|
||||||
|
|
||||||
props.canvas.body.nodes.forEach((e) => {
|
props.canvas.body.nodes.forEach((e) => {
|
||||||
minX = Math.min(minX, e.x);
|
_minX = Math.min(_minX, e.x);
|
||||||
minY = Math.min(minY, e.y);
|
_minY = Math.min(_minY, e.y);
|
||||||
maxX = Math.max(maxX, e.x + e.width);
|
_maxX = Math.max(_maxX, e.x + e.width);
|
||||||
maxY = Math.max(maxY, e.y + e.height);
|
_maxY = Math.max(_maxY, e.y + e.height);
|
||||||
});
|
});
|
||||||
|
|
||||||
minZoom.value = Math.min((canvas.value?.clientWidth ?? 0) / (maxX - minX), (canvas.value?.clientHeight ?? 0) / (maxY - minY)) * 0.9;
|
minX.value = _minX = _minX - 32;
|
||||||
|
minY.value = _minY = _minY - 32;
|
||||||
|
maxX.value = _maxX = _maxX + 32;
|
||||||
|
maxY.value = _maxY = _maxY + 32;
|
||||||
|
|
||||||
centerX.value = (canvas.value?.clientWidth ?? 0) / 2;
|
minZoom.value = zoom.value = Math.min((canvas.value?.clientWidth ?? 0) / (_maxX - _minX), (canvas.value?.clientHeight ?? 0) / (_maxY - _minY)) * 0.9;
|
||||||
centerY.value = (canvas.value?.clientHeight ?? 0) / 2;
|
|
||||||
|
|
||||||
dispX.value = -(canvas.value?.clientWidth ?? 0) / 2;
|
bbox.value = canvas.value?.getBoundingClientRect();
|
||||||
dispY.value = -(canvas.value?.clientHeight ?? 0) / 2;
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
centerX.value = (bbox.value?.width ?? 0) / 2;
|
||||||
|
centerY.value = (bbox.value?.height ?? 0) / 2;
|
||||||
|
|
||||||
|
dispX.value = -(_maxX + _minX) / 2;
|
||||||
|
dispY.value = -(_maxY + _minY) / 2;
|
||||||
})
|
})
|
||||||
|
|
||||||
const onPointerDown = (event) => {
|
const onPointerDown = (event: PointerEvent) => {
|
||||||
if (event.isPrimary === false) return;
|
if (event.isPrimary === false) return;
|
||||||
event.target.setPointerCapture(event.pointerId);
|
|
||||||
dragging = true;
|
dragging = true;
|
||||||
|
|
||||||
posX = event.clientX;
|
posX = event.clientX;
|
||||||
|
|
@ -43,7 +54,7 @@ const onPointerDown = (event) => {
|
||||||
document.addEventListener('pointerup', onPointerUp);
|
document.addEventListener('pointerup', onPointerUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPointerMove = (event) => {
|
const onPointerMove = (event: PointerEvent) => {
|
||||||
if (event.isPrimary === false) return;
|
if (event.isPrimary === false) return;
|
||||||
dispX.value -= (posX - event.clientX) / zoom.value;
|
dispX.value -= (posX - event.clientX) / zoom.value;
|
||||||
dispY.value -= (posY - event.clientY) / zoom.value;
|
dispY.value -= (posY - event.clientY) / zoom.value;
|
||||||
|
|
@ -52,7 +63,7 @@ const onPointerMove = (event) => {
|
||||||
posY = event.clientY;
|
posY = event.clientY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPointerUp = (event) => {
|
const onPointerUp = (event: PointerEvent) => {
|
||||||
if (event.isPrimary === false) return;
|
if (event.isPrimary === false) return;
|
||||||
dragging = false;
|
dragging = false;
|
||||||
document.removeEventListener('pointermove', onPointerMove);
|
document.removeEventListener('pointermove', onPointerMove);
|
||||||
|
|
@ -68,6 +79,11 @@ const onWheel = (event: WheelEvent) => {
|
||||||
zoom.value = minZoom.value;
|
zoom.value = minZoom.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reset = (event: PointerEvent) => {
|
||||||
|
zoom.value = minZoom.value;
|
||||||
|
dispX.value = -(maxX.value + minX.value) / 2;
|
||||||
|
dispY.value = -(maxY.value + minY.value) / 2;
|
||||||
|
}
|
||||||
function clamp(x: number, min: number, max: number): number {
|
function clamp(x: number, min: number, max: number): number {
|
||||||
if (x > max)
|
if (x > max)
|
||||||
return max;
|
return max;
|
||||||
|
|
@ -115,7 +131,7 @@ function mK(e: { minX: number, minY: number, maxX: number, maxY: number }, t: 'b
|
||||||
return { x: e.minX, y: (e.minY + e.maxY) / 2 };
|
return { x: e.minX, y: (e.minY + e.maxY) / 2 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function bbox(node: CanvasNode): { minX: number, minY: number, maxX: number, maxY: number } {
|
function getBbox(node: CanvasNode): { minX: number, minY: number, maxX: number, maxY: number } {
|
||||||
return { minX: node.x, minY: node.y, maxX: node.x + node.width, maxY: node.y + node.height };
|
return { minX: node.x, minY: node.y, maxX: node.x + node.width, maxY: node.y + node.height };
|
||||||
}
|
}
|
||||||
function path(from: CanvasNode, fromSide: 'bottom' | 'top' | 'left' | 'right', to: CanvasNode, toSide: 'bottom' | 'top' | 'left' | 'right'): any {
|
function path(from: CanvasNode, fromSide: 'bottom' | 'top' | 'left' | 'right', to: CanvasNode, toSide: 'bottom' | 'top' | 'left' | 'right'): any {
|
||||||
|
|
@ -127,8 +143,8 @@ function path(from: CanvasNode, fromSide: 'bottom' | 'top' | 'left' | 'right', t
|
||||||
toSide: '',
|
toSide: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const a = mK(bbox(from), fromSide),
|
const a = mK(getBbox(from), fromSide),
|
||||||
l = mK(bbox(to), toSide);
|
l = mK(getBbox(to), toSide);
|
||||||
return bezier(a, fromSide, l, toSide);
|
return bezier(a, fromSide, l, toSide);
|
||||||
}
|
}
|
||||||
function bezier(from: { x: number, y: number }, fromSide: 'bottom' | 'top' | 'left' | 'right', to: { x: number, y: number }, toSide: 'bottom' | 'top' | 'left' | 'right'): any {
|
function bezier(from: { x: number, y: number }, fromSide: 'bottom' | 'top' | 'left' | 'right', to: { x: number, y: number }, toSide: 'bottom' | 'top' | 'left' | 'right'): any {
|
||||||
|
|
@ -142,7 +158,7 @@ function bezier(from: { x: number, y: number }, fromSide: 'bottom' | 'top' | 'le
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div @pointerdown="onPointerDown" @wheel.passive="onWheel"
|
<div ref="canvas" @pointerdown="onPointerDown" @wheel.passive="onWheel"
|
||||||
@touchstart.prevent="" @dragstart.prevent="" class="canvas-wrapper node-insert-event mod-zoomed-out">
|
@touchstart.prevent="" @dragstart.prevent="" class="canvas-wrapper node-insert-event mod-zoomed-out">
|
||||||
<div class="canvas-controls" style="z-index: 421;">
|
<div class="canvas-controls" style="z-index: 421;">
|
||||||
<div class="canvas-control-group">
|
<div class="canvas-control-group">
|
||||||
|
|
@ -158,7 +174,7 @@ function bezier(from: { x: number, y: number }, fromSide: 'bottom' | 'top' | 'le
|
||||||
<path d="M21 3v5h-5"></path>
|
<path d="M21 3v5h-5"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div @click="zoom = minZoom; dispX = -(canvas?.clientWidth ?? 0) / 2; dispY = -(canvas?.clientHeight ?? 0) / 2;" class="canvas-control-item" aria-label="Zoom to fit" data-tooltip-position="left">
|
<div @click="reset" class="canvas-control-item" aria-label="Zoom to fit" data-tooltip-position="left">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-maximize">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-maximize">
|
||||||
<path d="M8 3H5a2 2 0 0 0-2 2v3"></path>
|
<path d="M8 3H5a2 2 0 0 0-2 2v3"></path>
|
||||||
<path d="M21 8V5a2 2 0 0 0-2-2h-3"></path>
|
<path d="M21 8V5a2 2 0 0 0-2-2h-3"></path>
|
||||||
|
|
@ -173,7 +189,7 @@ function bezier(from: { x: number, y: number }, fromSide: 'bottom' | 'top' | 'le
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="canvas" class="canvas" :style="{transform: `translate(${centerX}px, ${centerY}px) scale(${zoom}) translate(${dispX}px, ${dispY}px)`}">
|
<div class="canvas" :style="{transform: `translate(${centerX}px, ${centerY}px) scale(${zoom}) translate(${dispX}px, ${dispY}px)`}">
|
||||||
<svg class="canvas-edges">
|
<svg class="canvas-edges">
|
||||||
<CanvasEdge v-for="edge of props.canvas.body.edges" :key="edge.id" :path="path(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide)" :color="edge.color"/>
|
<CanvasEdge v-for="edge of props.canvas.body.edges" :key="edge.id" :path="path(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide)" :color="edge.color"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="site-body-left-column">
|
<div class="site-body-left-column">
|
||||||
<div class="site-body-left-column-inner">
|
<div class="site-body-left-column-inner">
|
||||||
<NuxtLink class="site-body-left-column-site-logo" aria-label="Developer Documentation logo" :href="'/Home'">
|
<NuxtLink class="site-body-left-column-site-logo" aria-label="Developer Documentation logo" :href="'/'">
|
||||||
<img aria-hidden="true" src="https://publish-01.obsidian.md/access/caa27d6312fe5c26ebc657cc609543be/Assets/obsidian-lockup-docs.svg" style="">
|
<img aria-hidden="true" src="https://publish-01.obsidian.md/access/caa27d6312fe5c26ebc657cc609543be/Assets/obsidian-lockup-docs.svg" style="">
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink class="site-body-left-column-site-name" aria-label="Accueil" :href="'/Home'">Accueil</NuxtLink>
|
<NuxtLink class="site-body-left-column-site-name" aria-label="Accueil" :href="'/Home'">Accueil</NuxtLink>
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="tree-item-children">
|
<div class="tree-item-children">
|
||||||
<ContentNavigation v-slot="{ navigation }">
|
<ContentNavigation v-slot="{ navigation }">
|
||||||
<NavigationLink v-if="!!navigation" v-for="link of navigation" :link="link" />
|
<NavigationLink v-if="!!navigation" v-for="link of navigation.filter(e => !['/tags', '/'].includes(e?._path ?? ''))" :link="link" />
|
||||||
</ContentNavigation>
|
</ContentNavigation>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,14 @@ const collapsed = ref(!useRoute().path.startsWith(props.link._path));
|
||||||
<path d="M3 8L12 17L21 8"></path>
|
<path d="M3 8L12 17L21 8"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tree-item-inner">{{ props.link.title }}</div>
|
<div class="tree-item-inner">{{ link.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!collapsed" class="tree-item-children">
|
<div v-if="!collapsed" class="tree-item-children">
|
||||||
<NavigationLink v-if="hasChildren" v-for="link of props.link.children" :link="link"/>
|
<NavigationLink v-if="hasChildren" v-for="l of link.children" :link="l"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<NuxtLink v-else class="tree-item-self" :to="props.link._path" :active-class="'mod-active'">
|
<NuxtLink v-else class="tree-item-self" :to="link._path" :active-class="'mod-active'">
|
||||||
<div class="tree-item-inner">{{ props.link.title }}</div>
|
<div class="tree-item-inner">{{ link.title }}<span v-if="link"></span></div>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -19,7 +19,7 @@ const hasChildren = computed(() => {
|
||||||
<template>
|
<template>
|
||||||
<div class="tree-item">
|
<div class="tree-item">
|
||||||
<div class="tree-item-self" :class="{'is-clickable': hasChildren}" data-path="{{ props.link.title }}">
|
<div class="tree-item-self" :class="{'is-clickable': hasChildren}" data-path="{{ props.link.title }}">
|
||||||
<a class="tree-item-inner" :href="'#' + props.link.id">{{ props.link.text }}</a>
|
<NuxtLink no-prefetch class="tree-item-inner" :href="{hash: '#' + props.link.id}">{{ props.link.text }}</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="tree-item-children">
|
<div class="tree-item-children">
|
||||||
<TocLink v-if="hasChildren" v-for="link of props.link.children" :link="link" />
|
<TocLink v-if="hasChildren" v-for="link of props.link.children" :link="link" />
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ interface ParsedContentExtended extends Omit<ParsedContent, 'body'> {
|
||||||
body: MarkdownRoot | CanvasContent | null;
|
body: MarkdownRoot | CanvasContent | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tags = inject('tags') as ParsedContent;
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
href: {
|
href: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -16,7 +17,7 @@ const props = defineProps({
|
||||||
default: undefined,
|
default: undefined,
|
||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
function sluggify(s: string): string {
|
function sluggify(s: string): string {
|
||||||
return s
|
return s
|
||||||
|
|
@ -29,28 +30,20 @@ function sluggify(s: string): string {
|
||||||
function flatten(val: TocLink[]): TocLink[] {
|
function flatten(val: TocLink[]): TocLink[] {
|
||||||
return val.flatMap ? val?.flatMap((e: TocLink) => e.children ? [e, ...flatten(e.children)] : e) : val;
|
return val.flatMap ? val?.flatMap((e: TocLink) => e.children ? [e, ...flatten(e.children)] : e) : val;
|
||||||
}
|
}
|
||||||
|
function handleResult(result: ParsedContentExtended, tag: boolean = false) {
|
||||||
const link = (props.href.startsWith('/') ? '' : '/') + (props.href.includes('#') ? props.href.substring(0, props.href.indexOf('#')) : props.href).replace(/\..*$/, '').replace("/index", '');
|
|
||||||
const anchor = props.href.includes('#') ? props.href.substring(props.href.indexOf('#'), props.href.length) : '';
|
|
||||||
let content = ref<ParsedContentExtended>(), loading = ref(true);
|
|
||||||
|
|
||||||
if(props.href !== '')
|
|
||||||
{
|
|
||||||
queryContent().where({ _path: new RegExp("/" + sluggify(link) + '$', 'i') }).findOne().then((result: ParsedContentExtended) => {
|
|
||||||
content.value = result;
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
let body = result.body;
|
let body: MarkdownRoot | CanvasContent | null = JSON.parse(JSON.stringify(result.body));
|
||||||
|
|
||||||
if (anchor && result && body) {
|
if (anchor && result && body) {
|
||||||
if (result._type == 'markdown' && (body as MarkdownRoot).children) {
|
if (result._type == 'markdown' && (body as MarkdownRoot).children) {
|
||||||
body = body as MarkdownRoot;
|
body = body as MarkdownRoot;
|
||||||
const id = flatten(body?.toc?.links ?? []).find(e => "#" + sluggify(e.id) === anchor);
|
const id = flatten(body?.toc?.links ?? []).find(e => "#" + sluggify(e.id) === anchor.replaceAll('/', ''));
|
||||||
const tag = `h${id?.depth ?? 0}`;
|
const tag = `h${id?.depth ?? 0}`;
|
||||||
|
|
||||||
const startIdx = body.children.findIndex(e => e.tag === tag && e?.props?.id === id?.id) ?? 0;
|
const startIdx = body.children.findIndex(e => e.tag === tag && e?.props?.id === id?.id) ?? 0;
|
||||||
const nbr = body.children.findIndex((e, i) => i > startIdx && e.tag?.match(new RegExp(`h[1-${id?.depth ?? 1}]`)));
|
const nbr = body.children.findIndex((e, i) => i > startIdx && e.tag?.match(new RegExp(`h[1-${id?.depth ?? 1}]`)));
|
||||||
|
|
||||||
body.children = body.children.splice(startIdx, (nbr === -1 ? body.children.length : nbr) - startIdx);
|
body.children = [...body.children].splice(startIdx + (tag ? 1 : 0), (nbr === -1 ? body.children.length : nbr) - (startIdx + (tag ? 1 : 0)));
|
||||||
}
|
}
|
||||||
else if (result._type == 'canvas') {
|
else if (result._type == 'canvas') {
|
||||||
body = body as CanvasContent;
|
body = body as CanvasContent;
|
||||||
|
|
@ -60,9 +53,36 @@ if(props.href !== '')
|
||||||
body.edges = body.edges.filter(e => nodes?.includes(e.fromNode) && nodes?.includes(e.toNode));
|
body.edges = body.edges.filter(e => nodes?.includes(e.fromNode) && nodes?.includes(e.toNode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
|
||||||
|
content.value = JSON.parse(JSON.stringify({ ...result, body: body }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = (props.href.startsWith('/') ? '' : '/') + (props.href.includes('#') ? props.href.substring(0, props.href.indexOf('#')) : props.href).replace(/\..*$/, '').replace("/index", '');
|
||||||
|
const anchor = props.href.includes('#') ? props.href.substring(props.href.indexOf('#'), props.href.length) : '';
|
||||||
|
let content = ref<ParsedContentExtended | null | undefined>(null), loading = ref(false), isTag = ref(false);
|
||||||
|
|
||||||
|
function loadContent()
|
||||||
|
{
|
||||||
|
loading.value = true;
|
||||||
|
if(props.href !== '' && !props.href.startsWith('/tags#'))
|
||||||
|
{
|
||||||
|
queryContent().where({ _path: new RegExp("/" + sluggify(link) + '$', 'i') }).findOne().then(handleResult).catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
content.value = undefined;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isTag.value = true;
|
||||||
|
|
||||||
|
if(tags.value)
|
||||||
|
handleResult(tags.value, true);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loading.value = false;
|
||||||
|
content.value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hovered = ref(false), pos = ref<any>();
|
const hovered = ref(false), pos = ref<any>();
|
||||||
|
|
@ -81,10 +101,13 @@ function showPreview(e: Event, bbox: boolean) {
|
||||||
if(rect.right + 550 < window.innerWidth)
|
if(rect.right + 550 < window.innerWidth)
|
||||||
r.left = rect.left + "px";
|
r.left = rect.left + "px";
|
||||||
else
|
else
|
||||||
r.right = (window.innerWidth - rect.left + 4) + "px";
|
r.right = (window.innerWidth - rect.right + 4) + "px";
|
||||||
pos.value = r;
|
pos.value = r;
|
||||||
}
|
}
|
||||||
hovered.value = true;
|
hovered.value = true;
|
||||||
|
|
||||||
|
if(content.value === null)
|
||||||
|
loadContent();
|
||||||
}
|
}
|
||||||
function hidePreview(e: Event) {
|
function hidePreview(e: Event) {
|
||||||
timeout = setTimeout(() => hovered.value = false, 300);
|
timeout = setTimeout(() => hovered.value = false, 300);
|
||||||
|
|
@ -93,17 +116,17 @@ function hidePreview(e: Event) {
|
||||||
|
|
||||||
<template >
|
<template >
|
||||||
<template v-if="href !== ''">
|
<template v-if="href !== ''">
|
||||||
<NuxtLink custom no-prefetch v-slot="{ href: hrefSlot, navigate }" :href="{ path: content?._path ?? link, hash: anchor }" :target="target">
|
<NuxtLink custom no-prefetch v-slot="{ href: hrefSlot, navigate }" :href="isTag ? undefined : { path: content?._path ?? link, hash: anchor }" :target="target">
|
||||||
<a :href="hrefSlot" @click="navigate" @mouseenter="(e) => showPreview(e, true)" @mouseleave="hidePreview" v-bind="$attrs"><slot ></slot></a>
|
<a :href="hrefSlot" @click="(isTag ? (e: Event) => e.preventDefault() : navigate)" @mouseenter="(e) => showPreview(e, true)" @mouseleave="hidePreview" v-bind="$attrs"><slot ></slot></a>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<Teleport to="body" v-if="hovered && (loading || !!content)">
|
<Teleport to="body" v-if="hovered && (loading || !!content)">
|
||||||
<div class="popover hover-popover is-loaded" :style="pos" @mouseenter="(e) => showPreview(e, false)" @mouseleave="hidePreview">
|
<div class="popover hover-popover is-loaded" :style="pos" @mouseenter="(e) => showPreview(e, false)" @mouseleave="hidePreview">
|
||||||
<template v-if="!!content">
|
<template v-if="!!content">
|
||||||
<div class="markdown-embed" v-if="content._type == 'markdown' && ((content as ParsedContent)?.body?.children?.length ?? 0) > 0">
|
<div class="markdown-embed" :class="{'tag-embed': isTag}" v-if="content._type == 'markdown' && ((content as ParsedContent)?.body?.children?.length ?? 0) > 0">
|
||||||
<div class="markdown-embed-content">
|
<div class="markdown-embed-content">
|
||||||
<div class="markdown-preview-view markdown-rendered node-insert-event hide-title">
|
<div class="markdown-preview-view markdown-rendered node-insert-event hide-title">
|
||||||
<div class="markdown-preview-sizer markdown-preview-section" style="padding-bottom: 0px;">
|
<div class="markdown-preview-sizer markdown-preview-section" style="padding-bottom: 0px;">
|
||||||
<h1 v-if="content?.title">{{ content?.title }}</h1>
|
<h1 v-if="content?.title && !isTag">{{ content?.title }}</h1>
|
||||||
<ContentRenderer :key="content._id" :value="content"/>
|
<ContentRenderer :key="content._id" :value="content"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -116,12 +139,14 @@ function hidePreview(e: Event) {
|
||||||
<div class="not-found-container">
|
<div class="not-found-container">
|
||||||
<div class="not-found-image"></div>
|
<div class="not-found-image"></div>
|
||||||
<div class="not-found-title">Impossible de prévisualiser</div>
|
<div class="not-found-title">Impossible de prévisualiser</div>
|
||||||
<div class="not-found-description">Cliquez sur le lien pour accéder au contenu</div>
|
<div v-if="!isTag" class="not-found-description">Cliquez sur le lien pour accéder au contenu</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-else>
|
<div v-else class="preload" style="text-align:center">
|
||||||
|
<svg style="width:50px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<path style="transform-origin:50px 50px;animation:1s linear infinite rotate" fill="currentColor" d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="render-container-inner" >
|
|
||||||
<div class="publish-renderer">
|
|
||||||
<div class="markdown-preview-view markdown-rendered node-insert-event hide-title">
|
|
||||||
<div class="markdown-preview-sizer markdown-preview-section" style="padding-bottom: 0px;">
|
|
||||||
<h1>Bonjour :)</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra-title">
|
|
||||||
<span class="extra-title-text">Home</span>
|
|
||||||
<span aria-label="Close page" role="button">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-x">
|
|
||||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
||||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,13 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { page } = useContent()
|
const { page } = useContent()
|
||||||
|
useContentHead(page);
|
||||||
/*function toggleCollapse(e: HTMLElement)
|
|
||||||
{
|
|
||||||
e.classList.toggle('is-collapsed');
|
|
||||||
const children = [...e.children];
|
|
||||||
children.splice(0, 1);
|
|
||||||
children.forEach(e => e.classList);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.querySelectorAll('.callout.is-collapsible .callout-title').forEach(e => {
|
document.querySelectorAll('.callout.is-collapsible .callout-title').forEach(e => {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<template>
|
|
||||||
Tags
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
const query = await queryContent().where({
|
|
||||||
tags: { $exists: true }
|
|
||||||
}).find();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-for="article in query" :key="article._path">
|
|
||||||
<h2>{{ article.title }}</h2>
|
|
||||||
<p>{{ article.description }}</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
Loading…
Reference in New Issue