refactor(frontend): use composition api
This commit is contained in:
parent
3d4a90b08a
commit
0717afc312
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="ssazuxis">
|
||||
<div ref="el" class="ssazuxis">
|
||||
<header class="_button" :style="{ background: bg }" @click="showBody = !showBody">
|
||||
<div class="title"><div><slot name="header"></slot></div></div>
|
||||
<div class="divider"></div>
|
||||
@ -22,43 +22,55 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { miLocalStorage } from '@/local-storage';
|
||||
import { defaultStore } from '@/store';
|
||||
|
||||
const miLocalStoragePrefix = 'ui:folder:' as const;
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
expanded: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
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');
|
||||
const props = withDefaults(defineProps<{
|
||||
expanded?: boolean;
|
||||
persistKey?: string;
|
||||
}>(), {
|
||||
expanded: true,
|
||||
});
|
||||
|
||||
const el = shallowRef<HTMLDivElement>();
|
||||
const bg = ref<string | null>(null);
|
||||
const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded);
|
||||
|
||||
watch(showBody, () => {
|
||||
if (props.persistKey) {
|
||||
miLocalStorage.setItem(`${miLocalStoragePrefix}${props.persistKey}`, showBody.value ? 't' : 'f');
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
function getParentBg(el: Element | null): string {
|
||||
});
|
||||
|
||||
function enter(el: Element) {
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = 0;
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = elementHeight + 'px';
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -67,35 +79,10 @@ export default defineComponent({
|
||||
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 elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = 0;
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = elementHeight + 'px';
|
||||
},
|
||||
afterEnter(el) {
|
||||
el.style.height = null;
|
||||
},
|
||||
leave(el) {
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = elementHeight + 'px';
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = 0;
|
||||
},
|
||||
afterLeave(el) {
|
||||
el.style.height = null;
|
||||
},
|
||||
},
|
||||
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>
|
||||
|
||||
|
@ -54,8 +54,8 @@
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { reactive, shallowRef } from 'vue';
|
||||
import MkInput from './MkInput.vue';
|
||||
import MkTextarea from './MkTextarea.vue';
|
||||
import MkSwitch from './MkSwitch.vue';
|
||||
@ -66,58 +66,36 @@ import MkRadios from './MkRadios.vue';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkModalWindow,
|
||||
MkInput,
|
||||
MkTextarea,
|
||||
MkSwitch,
|
||||
MkSelect,
|
||||
MkRange,
|
||||
MkButton,
|
||||
MkRadios,
|
||||
},
|
||||
const props = defineProps<{
|
||||
title: string;
|
||||
form: any;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', v: {
|
||||
canceled?: boolean;
|
||||
result?: any;
|
||||
}): void;
|
||||
}>();
|
||||
|
||||
emits: ['done'],
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const values = reactive({});
|
||||
|
||||
data() {
|
||||
return {
|
||||
values: {},
|
||||
i18n,
|
||||
};
|
||||
},
|
||||
for (const item in props.form) {
|
||||
values[item] = props.form[item].default ?? null;
|
||||
}
|
||||
|
||||
created() {
|
||||
for (const item in this.form) {
|
||||
this.values[item] = this.form[item].default ?? null;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
ok() {
|
||||
this.$emit('done', {
|
||||
result: this.values,
|
||||
function ok() {
|
||||
emit('done', {
|
||||
result: values,
|
||||
});
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.$emit('done', {
|
||||
function cancel() {
|
||||
emit('done', {
|
||||
canceled: true,
|
||||
});
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
},
|
||||
});
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
@ -28,54 +28,38 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import number from '@/filters/number';
|
||||
import XValue from '@/components/MkObjectView.value.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'XValue',
|
||||
const props = defineProps<{
|
||||
value: any;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
const collapsed = reactive({});
|
||||
|
||||
setup(props) {
|
||||
const collapsed = reactive({});
|
||||
|
||||
if (isObject(props.value)) {
|
||||
if (isObject(props.value)) {
|
||||
for (const key in props.value) {
|
||||
collapsed[key] = collapsable(props.value[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isObject(v): boolean {
|
||||
function isObject(v): boolean {
|
||||
return typeof v === 'object' && !Array.isArray(v) && v !== null;
|
||||
}
|
||||
}
|
||||
|
||||
function isArray(v): boolean {
|
||||
function isArray(v): boolean {
|
||||
return Array.isArray(v);
|
||||
}
|
||||
}
|
||||
|
||||
function isEmpty(v): boolean {
|
||||
function isEmpty(v): boolean {
|
||||
return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0);
|
||||
}
|
||||
}
|
||||
|
||||
function collapsable(v): boolean {
|
||||
function collapsable(v): boolean {
|
||||
return (isObject(v) || isArray(v)) && !isEmpty(v);
|
||||
}
|
||||
|
||||
return {
|
||||
number,
|
||||
collapsed,
|
||||
isObject,
|
||||
isArray,
|
||||
isEmpty,
|
||||
collapsable,
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,37 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { VNode, defineComponent, h } from 'vue';
|
||||
import { VNode, defineComponent, h, ref, watch } from 'vue';
|
||||
import MkRadio from './MkRadio.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkRadio,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: this.modelValue,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.$emit('update:modelValue', this.value);
|
||||
},
|
||||
},
|
||||
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();
|
||||
setup(props, context) {
|
||||
const value = ref(props.modelValue);
|
||||
watch(value, () => {
|
||||
context.emit('update:modelValue', value.value);
|
||||
});
|
||||
if (!context.slots.default) return null;
|
||||
let options = context.slots.default();
|
||||
const label = context.slots.label && context.slots.label();
|
||||
const caption = context.slots.caption && context.slots.caption();
|
||||
|
||||
// なぜかFragmentになることがあるため
|
||||
if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
|
||||
|
||||
return h('div', {
|
||||
return () => h('div', {
|
||||
class: 'novjtcto',
|
||||
}, [
|
||||
...(label ? [h('div', {
|
||||
@ -42,8 +32,8 @@ export default defineComponent({
|
||||
}, options.map(option => h(MkRadio, {
|
||||
key: option.key,
|
||||
value: option.props?.value,
|
||||
modelValue: this.value,
|
||||
'onUpdate:modelValue': value => this.value = value,
|
||||
modelValue: value.value,
|
||||
'onUpdate:modelValue': _v => value.value = _v,
|
||||
}, () => option.children)),
|
||||
),
|
||||
...(caption ? [h('div', {
|
||||
|
@ -7,17 +7,17 @@ export default defineComponent({
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const options = this.$slots.default();
|
||||
setup(props, { emit, slots }) {
|
||||
const options = slots.default();
|
||||
|
||||
return h('div', {
|
||||
return () => h('div', {
|
||||
class: 'pxhvhrfw',
|
||||
}, 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,
|
||||
disabled: this.modelValue === option.props.value,
|
||||
disabled: props.modelValue === option.props.value,
|
||||
onClick: () => {
|
||||
this.$emit('update:modelValue', option.props.value);
|
||||
emit('update:modelValue', option.props.value);
|
||||
},
|
||||
}, option.children), [
|
||||
[resolveDirective('click-anime')],
|
||||
|
@ -15,31 +15,22 @@
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, watch } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { defaultStore } from '@/store';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
},
|
||||
const props = defineProps<{
|
||||
p: () => Promise<any>;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
p: {
|
||||
type: Function as PropType<() => Promise<any>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
const pending = ref(true);
|
||||
const resolved = ref(false);
|
||||
const rejected = ref(false);
|
||||
const result = ref(null);
|
||||
|
||||
setup(props, context) {
|
||||
const pending = ref(true);
|
||||
const resolved = ref(false);
|
||||
const rejected = ref(false);
|
||||
const result = ref(null);
|
||||
|
||||
const process = () => {
|
||||
const process = () => {
|
||||
if (props.p == null) {
|
||||
return;
|
||||
}
|
||||
@ -56,29 +47,17 @@ export default defineComponent({
|
||||
pending.value = false;
|
||||
rejected.value = true;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
watch(() => props.p, () => {
|
||||
watch(() => props.p, () => {
|
||||
process();
|
||||
}, {
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
const retry = () => {
|
||||
process();
|
||||
};
|
||||
|
||||
return {
|
||||
pending,
|
||||
resolved,
|
||||
rejected,
|
||||
result,
|
||||
retry,
|
||||
defaultStore,
|
||||
i18n,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const retry = () => {
|
||||
process();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,24 +1,7 @@
|
||||
import { h, defineComponent } from 'vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
tag: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'span',
|
||||
},
|
||||
textTag: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
render() {
|
||||
let str = this.src;
|
||||
export default function(props: { src: string; tag?: string; textTag?: string; }, { slots }) {
|
||||
let str = props.src;
|
||||
const parsed = [] as (string | { arg: string; })[];
|
||||
while (true) {
|
||||
const nextBracketOpen = str.indexOf('{');
|
||||
@ -37,6 +20,5 @@ export default defineComponent({
|
||||
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]()));
|
||||
},
|
||||
});
|
||||
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 Misskey from 'misskey-js';
|
||||
import MkUrl from '@/components/global/MkUrl.vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
import MkMention from '@/components/MkMention.vue';
|
||||
@ -21,46 +22,21 @@ border-left: solid 3px var(--fg);
|
||||
opacity: 0.7;
|
||||
`.split('\n').join(' ');
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
plain: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
nowrap: {
|
||||
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,
|
||||
}
|
||||
},
|
||||
export default function(props: {
|
||||
text: string;
|
||||
plain?: boolean;
|
||||
nowrap?: boolean;
|
||||
author?: Misskey.entities.UserLite;
|
||||
i?: Misskey.entities.UserLite;
|
||||
isNote?: boolean;
|
||||
emojiUrls?: string[];
|
||||
rootScale?: number;
|
||||
}) {
|
||||
const isNote = props.isNote !== undefined ? props.isNote : true;
|
||||
|
||||
render() {
|
||||
if (this.text == null || this.text === '') return;
|
||||
if (props.text == null || props.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) => {
|
||||
if (t == null) return null;
|
||||
@ -79,7 +55,7 @@ export default defineComponent({
|
||||
case 'text': {
|
||||
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||
|
||||
if (!this.plain) {
|
||||
if (!props.plain) {
|
||||
const res: (VNode | string)[] = [];
|
||||
for (const t of text.split('\n')) {
|
||||
res.push(h('br'));
|
||||
@ -280,7 +256,7 @@ export default defineComponent({
|
||||
case 'mention': {
|
||||
return [h(MkMention, {
|
||||
key: Math.random(),
|
||||
host: (token.props.host == null && this.author && this.author.host != null ? this.author.host : token.props.host) || host,
|
||||
host: (token.props.host == null && props.author && props.author.host != null ? props.author.host : token.props.host) || host,
|
||||
username: token.props.username,
|
||||
})];
|
||||
}
|
||||
@ -288,7 +264,7 @@ export default defineComponent({
|
||||
case 'hashtag': {
|
||||
return [h(MkA, {
|
||||
key: Math.random(),
|
||||
to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
|
||||
to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
|
||||
style: 'color:var(--hashtag);',
|
||||
}, `#${token.props.hashtag}`)];
|
||||
}
|
||||
@ -310,7 +286,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
case 'quote': {
|
||||
if (!this.nowrap) {
|
||||
if (!props.nowrap) {
|
||||
return [h('div', {
|
||||
style: QUOTE_STYLE,
|
||||
}, genEl(token.children, scale))];
|
||||
@ -323,26 +299,26 @@ export default defineComponent({
|
||||
|
||||
case 'emojiCode': {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this.author?.host == null) {
|
||||
if (props.author?.host == null) {
|
||||
return [h(MkCustomEmoji, {
|
||||
key: Math.random(),
|
||||
name: token.props.name,
|
||||
normal: this.plain,
|
||||
normal: props.plain,
|
||||
host: null,
|
||||
useOriginalSize: scale >= 2.5,
|
||||
})];
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this.emojiUrls && (this.emojiUrls[token.props.name] == null)) {
|
||||
if (props.emojiUrls && (props.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,
|
||||
url: props.emojiUrls ? props.emojiUrls[token.props.name] : null,
|
||||
normal: props.plain,
|
||||
host: props.author.host,
|
||||
useOriginalSize: scale >= 2.5,
|
||||
})];
|
||||
}
|
||||
@ -384,7 +360,5 @@ export default defineComponent({
|
||||
}
|
||||
}).flat(Infinity) as (VNode | string)[];
|
||||
|
||||
// Parse ast to DOM
|
||||
return h('span', genEl(ast, this.rootScale));
|
||||
},
|
||||
});
|
||||
return h('span', genEl(ast, props.rootScale ?? 1));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user