feat: UiProse prose component with link highjacking
This commit is contained in:
@@ -6,6 +6,6 @@ defineProps<SectionTextBlockFragment>();
|
||||
|
||||
<template>
|
||||
<LayoutContained data-section-type="text-block" v-bind="layoutSettings!">
|
||||
<div v-html="content" />
|
||||
<UiProse :content="content" />
|
||||
</LayoutContained>
|
||||
</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
|
||||
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
|
||||
export default defineNuxtConfig({
|
||||
|
||||
@@ -5,6 +7,7 @@ export default defineNuxtConfig({
|
||||
"@lewebsimple/nuxt-graphql",
|
||||
"@nuxt/eslint",
|
||||
"@nuxt/ui",
|
||||
"@nuxtjs/seo",
|
||||
"nuxt-auth-utils",
|
||||
],
|
||||
|
||||
@@ -18,6 +21,12 @@ export default defineNuxtConfig({
|
||||
|
||||
css: ["~/assets/css/_main.css"],
|
||||
|
||||
site: {
|
||||
name: description,
|
||||
url: process.env.NUXT_SITE_URL || "https://wp-headless.ledevsimple.ca",
|
||||
defaultLocale: "fr",
|
||||
},
|
||||
|
||||
ui: {
|
||||
colorMode: false,
|
||||
},
|
||||
@@ -47,4 +56,5 @@ export default defineNuxtConfig({
|
||||
},
|
||||
saveSdl: "server/graphql/schema.graphql",
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"@iconify-json/lucide": "^1.2.84",
|
||||
"@lewebsimple/nuxt-graphql": "^0.4.0",
|
||||
"@nuxt/ui": "4.3.0",
|
||||
"@nuxtjs/seo": "^3.3.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"nuxt": "^4.2.2",
|
||||
"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