PronounsPage/components/LinkedText.vue
2025-04-27 15:58:14 +02:00

129 lines
3.6 KiB
Vue

<script lang="ts">
import { defineComponent, h } from 'vue';
import type { VNode } from 'vue';
import useConfig from '../composables/useConfig.ts';
import useSpelling from '../composables/useSpelling.ts';
import { escapeHtml } from '../src/helpers.ts';
import Icon from './Icon.vue';
import { NuxtLink } from '#components';
export default defineComponent({
props: {
text: { default: '', type: String },
noicons: { type: Boolean },
escape: { type: Boolean },
},
setup() {
const { handleSpelling } = useSpelling();
return {
config: useConfig(),
handleSpelling,
};
},
render() {
let text = this.text;
if (this.escape) {
text = escapeHtml(text);
}
if (!text) {
return h('span', this.$attrs);
}
let isLink = false;
let isIcon = false;
let isEscape = false;
let buffer = '';
let linkBuffer = '';
const children: VNode[] = [];
const buildLink = (): VNode => {
if (isIcon) {
return h(Icon, { v: buffer });
}
const attrs = { ...this.$attrs, innerHTML: this.handleSpelling(buffer) };
if (!isLink) {
return h('span', attrs);
}
linkBuffer = linkBuffer.replace(/≡/g, '='); // meh workaround, i know…
if (linkBuffer === '') {
linkBuffer = `#${buffer}`;
}
if (linkBuffer.startsWith('https://') ||
linkBuffer.startsWith('http://') ||
linkBuffer.startsWith('mailto:') ||
linkBuffer.endsWith('.pdf')
) {
return h(
'a',
{ ...attrs, href: linkBuffer, target: '_blank', rel: 'noopener' },
);
}
if (linkBuffer.indexOf('#') === 0) {
return h('a', { ...attrs, href: linkBuffer });
}
const to = encodeURI(linkBuffer) || `/${this.config.nouns.route}#${this.handleSpelling(buffer)}`;
return h(NuxtLink, { ...attrs, to });
};
const addChild = (): void => {
if (!buffer) {
return;
}
children.push(buildLink());
buffer = '';
linkBuffer = '';
};
for (const c of text) {
if (c === '{') {
addChild();
isLink = true;
continue;
} else if (c === '}') {
addChild();
isLink = false;
continue;
} else if (isLink && c === '=') {
if (linkBuffer) {
linkBuffer += '=';
}
linkBuffer += buffer;
buffer = '';
continue;
} else if (c === '[' && !this.noicons) {
addChild();
if (isEscape) {
isEscape = false;
} else {
isIcon = true;
continue;
}
} else if (c === ']' && !this.noicons) {
addChild();
if (isIcon) {
isIcon = false;
continue;
}
} else if (c === '\\') {
isEscape = true;
continue;
} else if (isEscape) {
buffer += '\\';
isEscape = false;
}
buffer += c;
}
addChild();
return children;
},
});
</script>