feat: UiProse prose component with link highjacking
This commit is contained in:
@@ -6,6 +6,6 @@ defineProps<SectionTextBlockFragment>();
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutContained data-section-type="text-block" v-bind="layoutSettings!">
|
<LayoutContained data-section-type="text-block" v-bind="layoutSettings!">
|
||||||
<div v-html="content" />
|
<UiProse :content="content" />
|
||||||
</LayoutContained>
|
</LayoutContained>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ content: string }>();
|
||||||
|
const refContent = useTemplateRef("refContent");
|
||||||
|
useProseLinks(refContent);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="refContent" class="prose" v-html="content" />
|
||||||
|
</template>
|
||||||
65
wp-content/themes/moonshine/app/composables/useProseLinks.ts
Normal file
65
wp-content/themes/moonshine/app/composables/useProseLinks.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
export function useProseLinks(refContent: Ref<HTMLElement | null>) {
|
||||||
|
const router = useRouter();
|
||||||
|
const { url } = useSiteConfig();
|
||||||
|
const siteUrl = new URL(url);
|
||||||
|
|
||||||
|
// Determine if the href is internal
|
||||||
|
const isInternal = (href: string) => {
|
||||||
|
if (!href) return false;
|
||||||
|
if (href.startsWith("/")) return true;
|
||||||
|
if (href.startsWith("#")) return false;
|
||||||
|
try {
|
||||||
|
const hrefUrl = new URL(href);
|
||||||
|
return hrefUrl.hostname === siteUrl.hostname;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert href to relative path
|
||||||
|
const convertToRelative = (href: string) => {
|
||||||
|
if (href.startsWith("/")) return href;
|
||||||
|
try {
|
||||||
|
const hrefUrl = new URL(href);
|
||||||
|
if (hrefUrl.hostname === siteUrl.hostname) {
|
||||||
|
return hrefUrl.pathname + hrefUrl.search + hrefUrl.hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// Invalid URL
|
||||||
|
}
|
||||||
|
return href;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Highjack click events to use router for internal links
|
||||||
|
const handleClick = (e: MouseEvent) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const link = target.closest("a");
|
||||||
|
if (!link) return;
|
||||||
|
const href = link.getAttribute("href");
|
||||||
|
if (!href) return;
|
||||||
|
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || link.target === "_blank" || link.hasAttribute("download")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isInternal(href)) {
|
||||||
|
e.preventDefault();
|
||||||
|
const path = convertToRelative(href);
|
||||||
|
router.push(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attach and detach event listeners
|
||||||
|
onMounted(() => {
|
||||||
|
const element = unref(refContent);
|
||||||
|
if (element) {
|
||||||
|
element.addEventListener("click", handleClick);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const element = unref(refContent);
|
||||||
|
if (element) {
|
||||||
|
element.removeEventListener("click", handleClick);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,3 +2,6 @@
|
|||||||
|
|
||||||
// Core
|
// Core
|
||||||
require_once __DIR__ . '/includes/core/theme-setup.php';
|
require_once __DIR__ . '/includes/core/theme-setup.php';
|
||||||
|
|
||||||
|
// Vendors
|
||||||
|
require_once __DIR__ . '/includes/vendors/tinymce.php';
|
||||||
|
|||||||
13
wp-content/themes/moonshine/includes/vendors/tinymce.php
vendored
Normal file
13
wp-content/themes/moonshine/includes/vendors/tinymce.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Convert absolute URLs to relative in link query
|
||||||
|
add_filter( 'wp_link_query', 'moonshine_tinymce_relative_urls' );
|
||||||
|
function moonshine_tinymce_relative_urls( $results ) {
|
||||||
|
foreach ( $results as &$result ) {
|
||||||
|
if ( empty( $result['permalink'] ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$result['permalink'] = str_replace( get_home_url(), '', $result['permalink'] );
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { description } from "../../../composer.json";
|
||||||
|
|
||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
|
||||||
@@ -5,6 +7,7 @@ export default defineNuxtConfig({
|
|||||||
"@lewebsimple/nuxt-graphql",
|
"@lewebsimple/nuxt-graphql",
|
||||||
"@nuxt/eslint",
|
"@nuxt/eslint",
|
||||||
"@nuxt/ui",
|
"@nuxt/ui",
|
||||||
|
"@nuxtjs/seo",
|
||||||
"nuxt-auth-utils",
|
"nuxt-auth-utils",
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -18,6 +21,12 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
css: ["~/assets/css/_main.css"],
|
css: ["~/assets/css/_main.css"],
|
||||||
|
|
||||||
|
site: {
|
||||||
|
name: description,
|
||||||
|
url: process.env.NUXT_SITE_URL || "https://wp-headless.ledevsimple.ca",
|
||||||
|
defaultLocale: "fr",
|
||||||
|
},
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
colorMode: false,
|
colorMode: false,
|
||||||
},
|
},
|
||||||
@@ -47,4 +56,5 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
saveSdl: "server/graphql/schema.graphql",
|
saveSdl: "server/graphql/schema.graphql",
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"@iconify-json/lucide": "^1.2.84",
|
"@iconify-json/lucide": "^1.2.84",
|
||||||
"@lewebsimple/nuxt-graphql": "^0.4.0",
|
"@lewebsimple/nuxt-graphql": "^0.4.0",
|
||||||
"@nuxt/ui": "4.3.0",
|
"@nuxt/ui": "4.3.0",
|
||||||
|
"@nuxtjs/seo": "^3.3.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"nuxt": "^4.2.2",
|
"nuxt": "^4.2.2",
|
||||||
"nuxt-auth-utils": "^0.5.27",
|
"nuxt-auth-utils": "^0.5.27",
|
||||||
|
|||||||
807
wp-content/themes/moonshine/pnpm-lock.yaml
generated
807
wp-content/themes/moonshine/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
|||||||
User-Agent: *
|
|
||||||
Disallow:
|
|
||||||
Reference in New Issue
Block a user