refactor(frontend): use composition api
This commit is contained in:
parent
3d4a90b08a
commit
0717afc312
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ssazuxis">
|
<div ref="el" class="ssazuxis">
|
||||||
<header class="_button" :style="{ background: bg }" @click="showBody = !showBody">
|
<header class="_button" :style="{ background: bg }" @click="showBody = !showBody">
|
||||||
<div class="title"><div><slot name="header"></slot></div></div>
|
<div class="title"><div><slot name="header"></slot></div></div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
@ -22,80 +22,67 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import { miLocalStorage } from '@/local-storage';
|
import { miLocalStorage } from '@/local-storage';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
const miLocalStoragePrefix = 'ui:folder:' as const;
|
const miLocalStoragePrefix = 'ui:folder:' as const;
|
||||||
|
|
||||||
export default defineComponent({
|
const props = withDefaults(defineProps<{
|
||||||
props: {
|
expanded?: boolean;
|
||||||
expanded: {
|
persistKey?: string;
|
||||||
type: Boolean,
|
}>(), {
|
||||||
required: false,
|
expanded: true,
|
||||||
default: true,
|
});
|
||||||
},
|
|
||||||
persistKey: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
defaultStore,
|
|
||||||
bg: null,
|
|
||||||
showBody: (this.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${this.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${this.persistKey}`) === 't') : this.expanded,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
showBody() {
|
|
||||||
if (this.persistKey) {
|
|
||||||
miLocalStorage.setItem(`${miLocalStoragePrefix}${this.persistKey}`, this.showBody ? 't' : 'f');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
function getParentBg(el: Element | null): string {
|
|
||||||
if (el == null || el.tagName === 'BODY') return 'var(--bg)';
|
|
||||||
const bg = el.style.background || el.style.backgroundColor;
|
|
||||||
if (bg) {
|
|
||||||
return bg;
|
|
||||||
} else {
|
|
||||||
return getParentBg(el.parentElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rawBg = getParentBg(this.$el);
|
|
||||||
const bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
|
||||||
bg.setAlpha(0.85);
|
|
||||||
this.bg = bg.toRgbString();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleContent(show: boolean) {
|
|
||||||
this.showBody = show;
|
|
||||||
},
|
|
||||||
|
|
||||||
enter(el) {
|
const el = shallowRef<HTMLDivElement>();
|
||||||
const elementHeight = el.getBoundingClientRect().height;
|
const bg = ref<string | null>(null);
|
||||||
el.style.height = 0;
|
const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded);
|
||||||
el.offsetHeight; // reflow
|
|
||||||
el.style.height = elementHeight + 'px';
|
watch(showBody, () => {
|
||||||
},
|
if (props.persistKey) {
|
||||||
afterEnter(el) {
|
miLocalStorage.setItem(`${miLocalStoragePrefix}${props.persistKey}`, showBody.value ? 't' : 'f');
|
||||||
el.style.height = null;
|
}
|
||||||
},
|
});
|
||||||
leave(el) {
|
|
||||||
const elementHeight = el.getBoundingClientRect().height;
|
function enter(el: Element) {
|
||||||
el.style.height = elementHeight + 'px';
|
const elementHeight = el.getBoundingClientRect().height;
|
||||||
el.offsetHeight; // reflow
|
el.style.height = 0;
|
||||||
el.style.height = 0;
|
el.offsetHeight; // reflow
|
||||||
},
|
el.style.height = elementHeight + 'px';
|
||||||
afterLeave(el) {
|
}
|
||||||
el.style.height = null;
|
|
||||||
},
|
function afterEnter(el: Element) {
|
||||||
},
|
el.style.height = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function leave(el: Element) {
|
||||||
|
const elementHeight = el.getBoundingClientRect().height;
|
||||||
|
el.style.height = elementHeight + 'px';
|
||||||
|
el.offsetHeight; // reflow
|
||||||
|
el.style.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterLeave(el: Element) {
|
||||||
|
el.style.height = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
function getParentBg(el: HTMLElement | null): string {
|
||||||
|
if (el == null || el.tagName === 'BODY') return 'var(--bg)';
|
||||||
|
const bg = el.style.background || el.style.backgroundColor;
|
||||||
|
if (bg) {
|
||||||
|
return bg;
|
||||||
|
} else {
|
||||||
|
return getParentBg(el.parentElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const rawBg = getParentBg(el.value);
|
||||||
|
const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||||
|
_bg.setAlpha(0.85);
|
||||||
|
bg.value = _bg.toRgbString();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@
|
||||||
</MkModalWindow>
|
</MkModalWindow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { reactive, shallowRef } from 'vue';
|
||||||
import MkInput from './MkInput.vue';
|
import MkInput from './MkInput.vue';
|
||||||
import MkTextarea from './MkTextarea.vue';
|
import MkTextarea from './MkTextarea.vue';
|
||||||
import MkSwitch from './MkSwitch.vue';
|
import MkSwitch from './MkSwitch.vue';
|
||||||
|
@ -66,58 +66,36 @@ import MkRadios from './MkRadios.vue';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
components: {
|
title: string;
|
||||||
MkModalWindow,
|
form: any;
|
||||||
MkInput,
|
}>();
|
||||||
MkTextarea,
|
|
||||||
MkSwitch,
|
|
||||||
MkSelect,
|
|
||||||
MkRange,
|
|
||||||
MkButton,
|
|
||||||
MkRadios,
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
const emit = defineEmits<{
|
||||||
title: {
|
(ev: 'done', v: {
|
||||||
type: String,
|
canceled?: boolean;
|
||||||
required: true,
|
result?: any;
|
||||||
},
|
}): void;
|
||||||
form: {
|
}>();
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['done'],
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
|
const values = reactive({});
|
||||||
|
|
||||||
data() {
|
for (const item in props.form) {
|
||||||
return {
|
values[item] = props.form[item].default ?? null;
|
||||||
values: {},
|
}
|
||||||
i18n,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
function ok() {
|
||||||
for (const item in this.form) {
|
emit('done', {
|
||||||
this.values[item] = this.form[item].default ?? null;
|
result: values,
|
||||||
}
|
});
|
||||||
},
|
dialog.value.close();
|
||||||
|
}
|
||||||
|
|
||||||
methods: {
|
function cancel() {
|
||||||
ok() {
|
emit('done', {
|
||||||
this.$emit('done', {
|
canceled: true,
|
||||||
result: this.values,
|
});
|
||||||
});
|
dialog.value.close();
|
||||||
this.$refs.dialog.close();
|
}
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.$emit('done', {
|
|
||||||
canceled: true,
|
|
||||||
});
|
|
||||||
this.$refs.dialog.close();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -28,54 +28,38 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import number from '@/filters/number';
|
import number from '@/filters/number';
|
||||||
|
import XValue from '@/components/MkObjectView.value.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
name: 'XValue',
|
value: any;
|
||||||
|
}>();
|
||||||
|
|
||||||
props: {
|
const collapsed = reactive({});
|
||||||
value: {
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
if (isObject(props.value)) {
|
||||||
const collapsed = reactive({});
|
for (const key in props.value) {
|
||||||
|
collapsed[key] = collapsable(props.value[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isObject(props.value)) {
|
function isObject(v): boolean {
|
||||||
for (const key in props.value) {
|
return typeof v === 'object' && !Array.isArray(v) && v !== null;
|
||||||
collapsed[key] = collapsable(props.value[key]);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isObject(v): boolean {
|
function isArray(v): boolean {
|
||||||
return typeof v === 'object' && !Array.isArray(v) && v !== null;
|
return Array.isArray(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isArray(v): boolean {
|
function isEmpty(v): boolean {
|
||||||
return Array.isArray(v);
|
return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmpty(v): boolean {
|
function collapsable(v): boolean {
|
||||||
return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0);
|
return (isObject(v) || isArray(v)) && !isEmpty(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapsable(v): boolean {
|
|
||||||
return (isObject(v) || isArray(v)) && !isEmpty(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
number,
|
|
||||||
collapsed,
|
|
||||||
isObject,
|
|
||||||
isArray,
|
|
||||||
isEmpty,
|
|
||||||
collapsable,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -1,37 +1,27 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { VNode, defineComponent, h } from 'vue';
|
import { VNode, defineComponent, h, ref, watch } from 'vue';
|
||||||
import MkRadio from './MkRadio.vue';
|
import MkRadio from './MkRadio.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
|
||||||
MkRadio,
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, context) {
|
||||||
return {
|
const value = ref(props.modelValue);
|
||||||
value: this.modelValue,
|
watch(value, () => {
|
||||||
};
|
context.emit('update:modelValue', value.value);
|
||||||
},
|
});
|
||||||
watch: {
|
if (!context.slots.default) return null;
|
||||||
value() {
|
let options = context.slots.default();
|
||||||
this.$emit('update:modelValue', this.value);
|
const label = context.slots.label && context.slots.label();
|
||||||
},
|
const caption = context.slots.caption && context.slots.caption();
|
||||||
},
|
|
||||||
render() {
|
|
||||||
console.log(this.$slots, this.$slots.label && this.$slots.label());
|
|
||||||
if (!this.$slots.default) return null;
|
|
||||||
let options = this.$slots.default();
|
|
||||||
const label = this.$slots.label && this.$slots.label();
|
|
||||||
const caption = this.$slots.caption && this.$slots.caption();
|
|
||||||
|
|
||||||
// なぜかFragmentになることがあるため
|
// なぜかFragmentになることがあるため
|
||||||
if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
|
if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
|
||||||
|
|
||||||
return h('div', {
|
return () => h('div', {
|
||||||
class: 'novjtcto',
|
class: 'novjtcto',
|
||||||
}, [
|
}, [
|
||||||
...(label ? [h('div', {
|
...(label ? [h('div', {
|
||||||
|
@ -42,8 +32,8 @@ export default defineComponent({
|
||||||
}, options.map(option => h(MkRadio, {
|
}, options.map(option => h(MkRadio, {
|
||||||
key: option.key,
|
key: option.key,
|
||||||
value: option.props?.value,
|
value: option.props?.value,
|
||||||
modelValue: this.value,
|
modelValue: value.value,
|
||||||
'onUpdate:modelValue': value => this.value = value,
|
'onUpdate:modelValue': _v => value.value = _v,
|
||||||
}, () => option.children)),
|
}, () => option.children)),
|
||||||
),
|
),
|
||||||
...(caption ? [h('div', {
|
...(caption ? [h('div', {
|
||||||
|
|
|
@ -7,17 +7,17 @@ export default defineComponent({
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
setup(props, { emit, slots }) {
|
||||||
const options = this.$slots.default();
|
const options = slots.default();
|
||||||
|
|
||||||
return h('div', {
|
return () => h('div', {
|
||||||
class: 'pxhvhrfw',
|
class: 'pxhvhrfw',
|
||||||
}, options.map(option => withDirectives(h('button', {
|
}, options.map(option => withDirectives(h('button', {
|
||||||
class: ['_button', { active: this.modelValue === option.props.value }],
|
class: ['_button', { active: props.modelValue === option.props.value }],
|
||||||
key: option.key,
|
key: option.key,
|
||||||
disabled: this.modelValue === option.props.value,
|
disabled: props.modelValue === option.props.value,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.$emit('update:modelValue', option.props.value);
|
emit('update:modelValue', option.props.value);
|
||||||
},
|
},
|
||||||
}, option.children), [
|
}, option.children), [
|
||||||
[resolveDirective('click-anime')],
|
[resolveDirective('click-anime')],
|
||||||
|
|
|
@ -15,70 +15,49 @@
|
||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent, PropType, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
components: {
|
p: () => Promise<any>;
|
||||||
MkButton,
|
}>();
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
const pending = ref(true);
|
||||||
p: {
|
const resolved = ref(false);
|
||||||
type: Function as PropType<() => Promise<any>>,
|
const rejected = ref(false);
|
||||||
required: true,
|
const result = ref(null);
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, context) {
|
const process = () => {
|
||||||
const pending = ref(true);
|
if (props.p == null) {
|
||||||
const resolved = ref(false);
|
return;
|
||||||
const rejected = ref(false);
|
}
|
||||||
const result = ref(null);
|
const promise = props.p();
|
||||||
|
pending.value = true;
|
||||||
|
resolved.value = false;
|
||||||
|
rejected.value = false;
|
||||||
|
promise.then((_result) => {
|
||||||
|
pending.value = false;
|
||||||
|
resolved.value = true;
|
||||||
|
result.value = _result;
|
||||||
|
});
|
||||||
|
promise.catch(() => {
|
||||||
|
pending.value = false;
|
||||||
|
rejected.value = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const process = () => {
|
watch(() => props.p, () => {
|
||||||
if (props.p == null) {
|
process();
|
||||||
return;
|
}, {
|
||||||
}
|
immediate: true,
|
||||||
const promise = props.p();
|
|
||||||
pending.value = true;
|
|
||||||
resolved.value = false;
|
|
||||||
rejected.value = false;
|
|
||||||
promise.then((_result) => {
|
|
||||||
pending.value = false;
|
|
||||||
resolved.value = true;
|
|
||||||
result.value = _result;
|
|
||||||
});
|
|
||||||
promise.catch(() => {
|
|
||||||
pending.value = false;
|
|
||||||
rejected.value = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(() => props.p, () => {
|
|
||||||
process();
|
|
||||||
}, {
|
|
||||||
immediate: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const retry = () => {
|
|
||||||
process();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
pending,
|
|
||||||
resolved,
|
|
||||||
rejected,
|
|
||||||
result,
|
|
||||||
retry,
|
|
||||||
defaultStore,
|
|
||||||
i18n,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const retry = () => {
|
||||||
|
process();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -1,42 +1,24 @@
|
||||||
import { h, defineComponent } from 'vue';
|
import { h } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default function(props: { src: string; tag?: string; textTag?: string; }, { slots }) {
|
||||||
props: {
|
let str = props.src;
|
||||||
src: {
|
const parsed = [] as (string | { arg: string; })[];
|
||||||
type: String,
|
while (true) {
|
||||||
required: true,
|
const nextBracketOpen = str.indexOf('{');
|
||||||
},
|
const nextBracketClose = str.indexOf('}');
|
||||||
tag: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'span',
|
|
||||||
},
|
|
||||||
textTag: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
let str = this.src;
|
|
||||||
const parsed = [] as (string | { arg: string; })[];
|
|
||||||
while (true) {
|
|
||||||
const nextBracketOpen = str.indexOf('{');
|
|
||||||
const nextBracketClose = str.indexOf('}');
|
|
||||||
|
|
||||||
if (nextBracketOpen === -1) {
|
if (nextBracketOpen === -1) {
|
||||||
parsed.push(str);
|
parsed.push(str);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (nextBracketOpen > 0) parsed.push(str.substr(0, nextBracketOpen));
|
if (nextBracketOpen > 0) parsed.push(str.substr(0, nextBracketOpen));
|
||||||
parsed.push({
|
parsed.push({
|
||||||
arg: str.substring(nextBracketOpen + 1, nextBracketClose),
|
arg: str.substring(nextBracketOpen + 1, nextBracketClose),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
str = str.substr(nextBracketClose + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return h(this.tag, parsed.map(x => typeof x === 'string' ? (this.textTag ? h(this.textTag, x) : x) : this.$slots[x.arg]()));
|
str = str.substr(nextBracketClose + 1);
|
||||||
},
|
}
|
||||||
});
|
|
||||||
|
return h(props.tag ?? 'span', parsed.map(x => typeof x === 'string' ? (props.textTag ? h(props.textTag, x) : x) : slots[x.arg]()));
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { VNode, defineComponent, h } from 'vue';
|
import { VNode, h } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import MkUrl from '@/components/global/MkUrl.vue';
|
import MkUrl from '@/components/global/MkUrl.vue';
|
||||||
import MkLink from '@/components/MkLink.vue';
|
import MkLink from '@/components/MkLink.vue';
|
||||||
import MkMention from '@/components/MkMention.vue';
|
import MkMention from '@/components/MkMention.vue';
|
||||||
|
@ -21,370 +22,343 @@ border-left: solid 3px var(--fg);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
`.split('\n').join(' ');
|
`.split('\n').join(' ');
|
||||||
|
|
||||||
export default defineComponent({
|
export default function(props: {
|
||||||
props: {
|
text: string;
|
||||||
text: {
|
plain?: boolean;
|
||||||
type: String,
|
nowrap?: boolean;
|
||||||
required: true,
|
author?: Misskey.entities.UserLite;
|
||||||
},
|
i?: Misskey.entities.UserLite;
|
||||||
plain: {
|
isNote?: boolean;
|
||||||
type: Boolean,
|
emojiUrls?: string[];
|
||||||
default: false,
|
rootScale?: number;
|
||||||
},
|
}) {
|
||||||
nowrap: {
|
const isNote = props.isNote !== undefined ? props.isNote : true;
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
author: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
i: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
isNote: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
emojiUrls: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
rootScale: {
|
|
||||||
type: Number,
|
|
||||||
default: 1,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
if (props.text == null || props.text === '') return;
|
||||||
if (this.text == null || this.text === '') return;
|
|
||||||
|
|
||||||
const ast = (this.plain ? mfm.parseSimple : mfm.parse)(this.text);
|
const ast = (props.plain ? mfm.parseSimple : mfm.parse)(props.text);
|
||||||
|
|
||||||
const validTime = (t: string | null | undefined) => {
|
const validTime = (t: string | null | undefined) => {
|
||||||
if (t == null) return null;
|
if (t == null) return null;
|
||||||
return t.match(/^[0-9.]+s$/) ? t : null;
|
return t.match(/^[0-9.]+s$/) ? t : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm;
|
const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gen Vue Elements from MFM AST
|
* Gen Vue Elements from MFM AST
|
||||||
* @param ast MFM AST
|
* @param ast MFM AST
|
||||||
* @param scale How times large the text is
|
* @param scale How times large the text is
|
||||||
*/
|
*/
|
||||||
const genEl = (ast: mfm.MfmNode[], scale: number) => ast.map((token): VNode | string | (VNode | string)[] => {
|
const genEl = (ast: mfm.MfmNode[], scale: number) => ast.map((token): VNode | string | (VNode | string)[] => {
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case 'text': {
|
case 'text': {
|
||||||
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||||
|
|
||||||
if (!this.plain) {
|
if (!props.plain) {
|
||||||
const res: (VNode | string)[] = [];
|
const res: (VNode | string)[] = [];
|
||||||
for (const t of text.split('\n')) {
|
for (const t of text.split('\n')) {
|
||||||
res.push(h('br'));
|
res.push(h('br'));
|
||||||
res.push(t);
|
res.push(t);
|
||||||
}
|
|
||||||
res.shift();
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return [text.replace(/\n/g, ' ')];
|
|
||||||
}
|
}
|
||||||
|
res.shift();
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return [text.replace(/\n/g, ' ')];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 'bold': {
|
case 'bold': {
|
||||||
return [h('b', genEl(token.children, scale))];
|
return [h('b', genEl(token.children, scale))];
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'strike': {
|
case 'strike': {
|
||||||
return [h('del', genEl(token.children, scale))];
|
return [h('del', genEl(token.children, scale))];
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'italic': {
|
case 'italic': {
|
||||||
return h('i', {
|
return h('i', {
|
||||||
style: 'font-style: oblique;',
|
style: 'font-style: oblique;',
|
||||||
}, genEl(token.children, scale));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'fn': {
|
case 'fn': {
|
||||||
// TODO: CSSを文字列で組み立てていくと token.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
|
// TODO: CSSを文字列で組み立てていくと token.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
|
||||||
let style;
|
let style;
|
||||||
switch (token.props.name) {
|
switch (token.props.name) {
|
||||||
case 'tada': {
|
case 'tada': {
|
||||||
const speed = validTime(token.props.args.speed) ?? '1s';
|
const speed = validTime(token.props.args.speed) ?? '1s';
|
||||||
style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both;` : '');
|
style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both;` : '');
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case 'jelly': {
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '1s';
|
|
||||||
style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'twitch': {
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '0.5s';
|
|
||||||
style = useAnim ? `animation: mfm-twitch ${speed} ease infinite;` : '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'shake': {
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '0.5s';
|
|
||||||
style = useAnim ? `animation: mfm-shake ${speed} ease infinite;` : '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'spin': {
|
|
||||||
const direction =
|
|
||||||
token.props.args.left ? 'reverse' :
|
|
||||||
token.props.args.alternate ? 'alternate' :
|
|
||||||
'normal';
|
|
||||||
const anime =
|
|
||||||
token.props.args.x ? 'mfm-spinX' :
|
|
||||||
token.props.args.y ? 'mfm-spinY' :
|
|
||||||
'mfm-spin';
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '1.5s';
|
|
||||||
style = useAnim ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'jump': {
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '0.75s';
|
|
||||||
style = useAnim ? `animation: mfm-jump ${speed} linear infinite;` : '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'bounce': {
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '0.75s';
|
|
||||||
style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'flip': {
|
|
||||||
const transform =
|
|
||||||
(token.props.args.h && token.props.args.v) ? 'scale(-1, -1)' :
|
|
||||||
token.props.args.v ? 'scaleY(-1)' :
|
|
||||||
'scaleX(-1)';
|
|
||||||
style = `transform: ${transform};`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'x2': {
|
|
||||||
return h('span', {
|
|
||||||
class: defaultStore.state.advancedMfm ? 'mfm-x2' : '',
|
|
||||||
}, genEl(token.children, scale * 2));
|
|
||||||
}
|
|
||||||
case 'x3': {
|
|
||||||
return h('span', {
|
|
||||||
class: defaultStore.state.advancedMfm ? 'mfm-x3' : '',
|
|
||||||
}, genEl(token.children, scale * 3));
|
|
||||||
}
|
|
||||||
case 'x4': {
|
|
||||||
return h('span', {
|
|
||||||
class: defaultStore.state.advancedMfm ? 'mfm-x4' : '',
|
|
||||||
}, genEl(token.children, scale * 4));
|
|
||||||
}
|
|
||||||
case 'font': {
|
|
||||||
const family =
|
|
||||||
token.props.args.serif ? 'serif' :
|
|
||||||
token.props.args.monospace ? 'monospace' :
|
|
||||||
token.props.args.cursive ? 'cursive' :
|
|
||||||
token.props.args.fantasy ? 'fantasy' :
|
|
||||||
token.props.args.emoji ? 'emoji' :
|
|
||||||
token.props.args.math ? 'math' :
|
|
||||||
null;
|
|
||||||
if (family) style = `font-family: ${family};`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'blur': {
|
|
||||||
return h('span', {
|
|
||||||
class: '_mfm_blur_',
|
|
||||||
}, genEl(token.children, scale));
|
|
||||||
}
|
|
||||||
case 'rainbow': {
|
|
||||||
const speed = validTime(token.props.args.speed) ?? '1s';
|
|
||||||
style = useAnim ? `animation: mfm-rainbow ${speed} linear infinite;` : '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'sparkle': {
|
|
||||||
if (!useAnim) {
|
|
||||||
return genEl(token.children, scale);
|
|
||||||
}
|
|
||||||
return h(MkSparkle, {}, genEl(token.children, scale));
|
|
||||||
}
|
|
||||||
case 'rotate': {
|
|
||||||
const degrees = parseFloat(token.props.args.deg ?? '90');
|
|
||||||
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'position': {
|
|
||||||
if (!defaultStore.state.advancedMfm) break;
|
|
||||||
const x = parseFloat(token.props.args.x ?? '0');
|
|
||||||
const y = parseFloat(token.props.args.y ?? '0');
|
|
||||||
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'scale': {
|
|
||||||
if (!defaultStore.state.advancedMfm) {
|
|
||||||
style = '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5);
|
|
||||||
const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5);
|
|
||||||
style = `transform: scale(${x}, ${y});`;
|
|
||||||
scale = scale * Math.max(x, y);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'fg': {
|
|
||||||
let color = token.props.args.color;
|
|
||||||
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
|
||||||
style = `color: #${color};`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'bg': {
|
|
||||||
let color = token.props.args.color;
|
|
||||||
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
|
||||||
style = `background-color: #${color};`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (style == null) {
|
case 'jelly': {
|
||||||
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
|
const speed = validTime(token.props.args.speed) ?? '1s';
|
||||||
} else {
|
style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'twitch': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.5s';
|
||||||
|
style = useAnim ? `animation: mfm-twitch ${speed} ease infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'shake': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.5s';
|
||||||
|
style = useAnim ? `animation: mfm-shake ${speed} ease infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'spin': {
|
||||||
|
const direction =
|
||||||
|
token.props.args.left ? 'reverse' :
|
||||||
|
token.props.args.alternate ? 'alternate' :
|
||||||
|
'normal';
|
||||||
|
const anime =
|
||||||
|
token.props.args.x ? 'mfm-spinX' :
|
||||||
|
token.props.args.y ? 'mfm-spinY' :
|
||||||
|
'mfm-spin';
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '1.5s';
|
||||||
|
style = useAnim ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'jump': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.75s';
|
||||||
|
style = useAnim ? `animation: mfm-jump ${speed} linear infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'bounce': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.75s';
|
||||||
|
style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'flip': {
|
||||||
|
const transform =
|
||||||
|
(token.props.args.h && token.props.args.v) ? 'scale(-1, -1)' :
|
||||||
|
token.props.args.v ? 'scaleY(-1)' :
|
||||||
|
'scaleX(-1)';
|
||||||
|
style = `transform: ${transform};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x2': {
|
||||||
return h('span', {
|
return h('span', {
|
||||||
style: 'display: inline-block; ' + style,
|
class: defaultStore.state.advancedMfm ? 'mfm-x2' : '',
|
||||||
|
}, genEl(token.children, scale * 2));
|
||||||
|
}
|
||||||
|
case 'x3': {
|
||||||
|
return h('span', {
|
||||||
|
class: defaultStore.state.advancedMfm ? 'mfm-x3' : '',
|
||||||
|
}, genEl(token.children, scale * 3));
|
||||||
|
}
|
||||||
|
case 'x4': {
|
||||||
|
return h('span', {
|
||||||
|
class: defaultStore.state.advancedMfm ? 'mfm-x4' : '',
|
||||||
|
}, genEl(token.children, scale * 4));
|
||||||
|
}
|
||||||
|
case 'font': {
|
||||||
|
const family =
|
||||||
|
token.props.args.serif ? 'serif' :
|
||||||
|
token.props.args.monospace ? 'monospace' :
|
||||||
|
token.props.args.cursive ? 'cursive' :
|
||||||
|
token.props.args.fantasy ? 'fantasy' :
|
||||||
|
token.props.args.emoji ? 'emoji' :
|
||||||
|
token.props.args.math ? 'math' :
|
||||||
|
null;
|
||||||
|
if (family) style = `font-family: ${family};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'blur': {
|
||||||
|
return h('span', {
|
||||||
|
class: '_mfm_blur_',
|
||||||
}, genEl(token.children, scale));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
}
|
case 'rainbow': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '1s';
|
||||||
case 'small': {
|
style = useAnim ? `animation: mfm-rainbow ${speed} linear infinite;` : '';
|
||||||
return [h('small', {
|
break;
|
||||||
style: 'opacity: 0.7;',
|
}
|
||||||
}, genEl(token.children, scale))];
|
case 'sparkle': {
|
||||||
}
|
if (!useAnim) {
|
||||||
|
return genEl(token.children, scale);
|
||||||
case 'center': {
|
}
|
||||||
return [h('div', {
|
return h(MkSparkle, {}, genEl(token.children, scale));
|
||||||
style: 'text-align:center;',
|
}
|
||||||
}, genEl(token.children, scale))];
|
case 'rotate': {
|
||||||
}
|
const degrees = parseFloat(token.props.args.deg ?? '90');
|
||||||
|
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
||||||
case 'url': {
|
break;
|
||||||
return [h(MkUrl, {
|
}
|
||||||
key: Math.random(),
|
case 'position': {
|
||||||
url: token.props.url,
|
if (!defaultStore.state.advancedMfm) break;
|
||||||
rel: 'nofollow noopener',
|
const x = parseFloat(token.props.args.x ?? '0');
|
||||||
})];
|
const y = parseFloat(token.props.args.y ?? '0');
|
||||||
}
|
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
||||||
|
break;
|
||||||
case 'link': {
|
}
|
||||||
return [h(MkLink, {
|
case 'scale': {
|
||||||
key: Math.random(),
|
if (!defaultStore.state.advancedMfm) {
|
||||||
url: token.props.url,
|
style = '';
|
||||||
rel: 'nofollow noopener',
|
break;
|
||||||
}, genEl(token.children, scale))];
|
}
|
||||||
}
|
const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5);
|
||||||
|
const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5);
|
||||||
case 'mention': {
|
style = `transform: scale(${x}, ${y});`;
|
||||||
return [h(MkMention, {
|
scale = scale * Math.max(x, y);
|
||||||
key: Math.random(),
|
break;
|
||||||
host: (token.props.host == null && this.author && this.author.host != null ? this.author.host : token.props.host) || host,
|
}
|
||||||
username: token.props.username,
|
case 'fg': {
|
||||||
})];
|
let color = token.props.args.color;
|
||||||
}
|
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||||
|
style = `color: #${color};`;
|
||||||
case 'hashtag': {
|
break;
|
||||||
return [h(MkA, {
|
}
|
||||||
key: Math.random(),
|
case 'bg': {
|
||||||
to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
|
let color = token.props.args.color;
|
||||||
style: 'color:var(--hashtag);',
|
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||||
}, `#${token.props.hashtag}`)];
|
style = `background-color: #${color};`;
|
||||||
}
|
break;
|
||||||
|
|
||||||
case 'blockCode': {
|
|
||||||
return [h(MkCode, {
|
|
||||||
key: Math.random(),
|
|
||||||
code: token.props.code,
|
|
||||||
lang: token.props.lang,
|
|
||||||
})];
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'inlineCode': {
|
|
||||||
return [h(MkCode, {
|
|
||||||
key: Math.random(),
|
|
||||||
code: token.props.code,
|
|
||||||
inline: true,
|
|
||||||
})];
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'quote': {
|
|
||||||
if (!this.nowrap) {
|
|
||||||
return [h('div', {
|
|
||||||
style: QUOTE_STYLE,
|
|
||||||
}, genEl(token.children, scale))];
|
|
||||||
} else {
|
|
||||||
return [h('span', {
|
|
||||||
style: QUOTE_STYLE,
|
|
||||||
}, genEl(token.children, scale))];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (style == null) {
|
||||||
|
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
|
||||||
|
} else {
|
||||||
|
return h('span', {
|
||||||
|
style: 'display: inline-block; ' + style,
|
||||||
|
}, genEl(token.children, scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 'emojiCode': {
|
case 'small': {
|
||||||
|
return [h('small', {
|
||||||
|
style: 'opacity: 0.7;',
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'center': {
|
||||||
|
return [h('div', {
|
||||||
|
style: 'text-align:center;',
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'url': {
|
||||||
|
return [h(MkUrl, {
|
||||||
|
key: Math.random(),
|
||||||
|
url: token.props.url,
|
||||||
|
rel: 'nofollow noopener',
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'link': {
|
||||||
|
return [h(MkLink, {
|
||||||
|
key: Math.random(),
|
||||||
|
url: token.props.url,
|
||||||
|
rel: 'nofollow noopener',
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mention': {
|
||||||
|
return [h(MkMention, {
|
||||||
|
key: Math.random(),
|
||||||
|
host: (token.props.host == null && props.author && props.author.host != null ? props.author.host : token.props.host) || host,
|
||||||
|
username: token.props.username,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'hashtag': {
|
||||||
|
return [h(MkA, {
|
||||||
|
key: Math.random(),
|
||||||
|
to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
|
||||||
|
style: 'color:var(--hashtag);',
|
||||||
|
}, `#${token.props.hashtag}`)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'blockCode': {
|
||||||
|
return [h(MkCode, {
|
||||||
|
key: Math.random(),
|
||||||
|
code: token.props.code,
|
||||||
|
lang: token.props.lang,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inlineCode': {
|
||||||
|
return [h(MkCode, {
|
||||||
|
key: Math.random(),
|
||||||
|
code: token.props.code,
|
||||||
|
inline: true,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'quote': {
|
||||||
|
if (!props.nowrap) {
|
||||||
|
return [h('div', {
|
||||||
|
style: QUOTE_STYLE,
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
} else {
|
||||||
|
return [h('span', {
|
||||||
|
style: QUOTE_STYLE,
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'emojiCode': {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (props.author?.host == null) {
|
||||||
|
return [h(MkCustomEmoji, {
|
||||||
|
key: Math.random(),
|
||||||
|
name: token.props.name,
|
||||||
|
normal: props.plain,
|
||||||
|
host: null,
|
||||||
|
useOriginalSize: scale >= 2.5,
|
||||||
|
})];
|
||||||
|
} else {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (this.author?.host == null) {
|
if (props.emojiUrls && (props.emojiUrls[token.props.name] == null)) {
|
||||||
|
return [h('span', `:${token.props.name}:`)];
|
||||||
|
} else {
|
||||||
return [h(MkCustomEmoji, {
|
return [h(MkCustomEmoji, {
|
||||||
key: Math.random(),
|
key: Math.random(),
|
||||||
name: token.props.name,
|
name: token.props.name,
|
||||||
normal: this.plain,
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
host: null,
|
url: props.emojiUrls ? props.emojiUrls[token.props.name] : null,
|
||||||
|
normal: props.plain,
|
||||||
|
host: props.author.host,
|
||||||
useOriginalSize: scale >= 2.5,
|
useOriginalSize: scale >= 2.5,
|
||||||
})];
|
})];
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
||||||
if (this.emojiUrls && (this.emojiUrls[token.props.name] == null)) {
|
|
||||||
return [h('span', `:${token.props.name}:`)];
|
|
||||||
} else {
|
|
||||||
return [h(MkCustomEmoji, {
|
|
||||||
key: Math.random(),
|
|
||||||
name: token.props.name,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
||||||
url: this.emojiUrls ? this.emojiUrls[token.props.name] : null,
|
|
||||||
normal: this.plain,
|
|
||||||
host: this.author.host,
|
|
||||||
useOriginalSize: scale >= 2.5,
|
|
||||||
})];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'unicodeEmoji': {
|
|
||||||
return [h(MkEmoji, {
|
|
||||||
key: Math.random(),
|
|
||||||
emoji: token.props.emoji,
|
|
||||||
})];
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'mathInline': {
|
|
||||||
return [h('code', token.props.formula)];
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'mathBlock': {
|
|
||||||
return [h('code', token.props.formula)];
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'search': {
|
|
||||||
return [h(MkGoogle, {
|
|
||||||
key: Math.random(),
|
|
||||||
q: token.props.query,
|
|
||||||
})];
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'plain': {
|
|
||||||
return [h('span', genEl(token.children, scale))];
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
console.error('unrecognized ast type:', (token as any).type);
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).flat(Infinity) as (VNode | string)[];
|
|
||||||
|
|
||||||
// Parse ast to DOM
|
case 'unicodeEmoji': {
|
||||||
return h('span', genEl(ast, this.rootScale));
|
return [h(MkEmoji, {
|
||||||
},
|
key: Math.random(),
|
||||||
});
|
emoji: token.props.emoji,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mathInline': {
|
||||||
|
return [h('code', token.props.formula)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mathBlock': {
|
||||||
|
return [h('code', token.props.formula)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'search': {
|
||||||
|
return [h(MkGoogle, {
|
||||||
|
key: Math.random(),
|
||||||
|
q: token.props.query,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'plain': {
|
||||||
|
return [h('span', genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
console.error('unrecognized ast type:', (token as any).type);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).flat(Infinity) as (VNode | string)[];
|
||||||
|
|
||||||
|
return h('span', genEl(ast, props.rootScale ?? 1));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue