refactor(frontend): use composition api
This commit is contained in:
parent
238d0fa667
commit
3d4a90b08a
|
@ -1,118 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="">
|
|
||||||
<div class="">
|
|
||||||
<MkInput v-model="text">
|
|
||||||
<template #label>Text</template>
|
|
||||||
</MkInput>
|
|
||||||
<MkSwitch v-model="flag">
|
|
||||||
<span>Switch is now {{ flag ? 'on' : 'off' }}</span>
|
|
||||||
</MkSwitch>
|
|
||||||
<div style="margin: 32px 0;">
|
|
||||||
<MkRadio v-model="radio" value="misskey">Misskey</MkRadio>
|
|
||||||
<MkRadio v-model="radio" value="mastodon">Mastodon</MkRadio>
|
|
||||||
<MkRadio v-model="radio" value="pleroma">Pleroma</MkRadio>
|
|
||||||
</div>
|
|
||||||
<MkButton inline>This is</MkButton>
|
|
||||||
<MkButton inline primary>the button</MkButton>
|
|
||||||
</div>
|
|
||||||
<div class="" style="pointer-events: none;">
|
|
||||||
<Mfm :text="mfm"/>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<MkButton inline primary @click="openMenu">Open menu</MkButton>
|
|
||||||
<MkButton inline primary @click="openDialog">Open dialog</MkButton>
|
|
||||||
<MkButton inline primary @click="openForm">Open form</MkButton>
|
|
||||||
<MkButton inline primary @click="openDrive">Open drive</MkButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import MkButton from '@/components/MkButton.vue';
|
|
||||||
import MkInput from '@/components/MkInput.vue';
|
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
|
||||||
import MkRadio from '@/components/MkRadio.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import * as config from '@/config';
|
|
||||||
import { $i } from '@/account';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
MkButton,
|
|
||||||
MkInput,
|
|
||||||
MkSwitch,
|
|
||||||
MkTextarea,
|
|
||||||
MkRadio,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
text: '',
|
|
||||||
flag: true,
|
|
||||||
radio: 'misskey',
|
|
||||||
$i,
|
|
||||||
mfm: `Hello world! This is an @example mention. BTW you are @${this.$i ? this.$i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async openDialog() {
|
|
||||||
os.alert({
|
|
||||||
type: 'warning',
|
|
||||||
title: 'Oh my Aichan',
|
|
||||||
text: 'Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async openForm() {
|
|
||||||
os.form('Example form', {
|
|
||||||
foo: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: true,
|
|
||||||
label: 'This is a boolean property',
|
|
||||||
},
|
|
||||||
bar: {
|
|
||||||
type: 'number',
|
|
||||||
default: 300,
|
|
||||||
label: 'This is a number property',
|
|
||||||
},
|
|
||||||
baz: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'Misskey makes you happy.',
|
|
||||||
label: 'This is a string property',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async openDrive() {
|
|
||||||
os.selectDriveFile(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
async selectUser() {
|
|
||||||
os.selectUser();
|
|
||||||
},
|
|
||||||
|
|
||||||
async openMenu(ev) {
|
|
||||||
os.popupMenu([{
|
|
||||||
type: 'label',
|
|
||||||
text: 'Fruits',
|
|
||||||
}, {
|
|
||||||
text: 'Create some apples',
|
|
||||||
action: () => {},
|
|
||||||
}, {
|
|
||||||
text: 'Read some oranges',
|
|
||||||
action: () => {},
|
|
||||||
}, {
|
|
||||||
text: 'Update some melons',
|
|
||||||
action: () => {},
|
|
||||||
}, null, {
|
|
||||||
text: 'Delete some bananas',
|
|
||||||
danger: true,
|
|
||||||
action: () => {},
|
|
||||||
}], ev.currentTarget ?? ev.target);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -23,22 +23,13 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
defineProps<{
|
||||||
props: {
|
def: any[];
|
||||||
def: {
|
grid?: boolean;
|
||||||
type: Array,
|
}>();
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -26,120 +26,71 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent, onMounted, nextTick, ref, watch, computed, toRefs } from 'vue';
|
import { onMounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue';
|
||||||
import { debounce } from 'throttle-debounce';
|
import { debounce } from 'throttle-debounce';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps<{
|
||||||
components: {
|
modelValue: string | null;
|
||||||
MkButton,
|
required?: boolean;
|
||||||
},
|
readonly?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
pattern?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
autofocus?: boolean;
|
||||||
|
autocomplete?: string;
|
||||||
|
spellcheck?: boolean;
|
||||||
|
debounce?: boolean;
|
||||||
|
manualSave?: boolean;
|
||||||
|
code?: boolean;
|
||||||
|
tall?: boolean;
|
||||||
|
pre?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
props: {
|
const emit = defineEmits<{
|
||||||
modelValue: {
|
(ev: 'change', _ev: KeyboardEvent): void;
|
||||||
required: true,
|
(ev: 'keydown', _ev: KeyboardEvent): void;
|
||||||
},
|
(ev: 'enter'): void;
|
||||||
type: {
|
(ev: 'update:modelValue', value: string): void;
|
||||||
type: String,
|
}>();
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
readonly: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
pattern: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
autofocus: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
autocomplete: {
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
spellcheck: {
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
tall: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
pre: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
debounce: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
manualSave: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['change', 'keydown', 'enter', 'update:modelValue'],
|
const { modelValue, autofocus } = toRefs(props);
|
||||||
|
const v = ref<string>(modelValue.value ?? '');
|
||||||
|
const focused = ref(false);
|
||||||
|
const changed = ref(false);
|
||||||
|
const invalid = ref(false);
|
||||||
|
const filled = computed(() => v.value !== '' && v.value != null);
|
||||||
|
const inputEl = shallowRef<HTMLTextAreaElement>();
|
||||||
|
|
||||||
setup(props, context) {
|
const focus = () => inputEl.value.focus();
|
||||||
const { modelValue, autofocus } = toRefs(props);
|
const onInput = (ev) => {
|
||||||
const v = ref(modelValue.value);
|
|
||||||
const focused = ref(false);
|
|
||||||
const changed = ref(false);
|
|
||||||
const invalid = ref(false);
|
|
||||||
const filled = computed(() => v.value !== '' && v.value != null);
|
|
||||||
const inputEl = ref(null);
|
|
||||||
|
|
||||||
const focus = () => inputEl.value.focus();
|
|
||||||
const onInput = (ev) => {
|
|
||||||
changed.value = true;
|
changed.value = true;
|
||||||
context.emit('change', ev);
|
emit('change', ev);
|
||||||
};
|
};
|
||||||
const onKeydown = (ev: KeyboardEvent) => {
|
const onKeydown = (ev: KeyboardEvent) => {
|
||||||
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
|
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
|
||||||
|
|
||||||
context.emit('keydown', ev);
|
emit('keydown', ev);
|
||||||
|
|
||||||
if (ev.code === 'Enter') {
|
if (ev.code === 'Enter') {
|
||||||
context.emit('enter');
|
emit('enter');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updated = () => {
|
const updated = () => {
|
||||||
changed.value = false;
|
changed.value = false;
|
||||||
context.emit('update:modelValue', v.value);
|
emit('update:modelValue', v.value ?? '');
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedUpdated = debounce(1000, updated);
|
const debouncedUpdated = debounce(1000, updated);
|
||||||
|
|
||||||
watch(modelValue, newValue => {
|
watch(modelValue, newValue => {
|
||||||
v.value = newValue;
|
v.value = newValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(v, newValue => {
|
watch(v, newValue => {
|
||||||
if (!props.manualSave) {
|
if (!props.manualSave) {
|
||||||
if (props.debounce) {
|
if (props.debounce) {
|
||||||
debouncedUpdated();
|
debouncedUpdated();
|
||||||
|
@ -149,30 +100,14 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid.value = inputEl.value.validity.badInput;
|
invalid.value = inputEl.value.validity.badInput;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (autofocus.value) {
|
if (autofocus.value) {
|
||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
v,
|
|
||||||
focused,
|
|
||||||
invalid,
|
|
||||||
changed,
|
|
||||||
filled,
|
|
||||||
inputEl,
|
|
||||||
focus,
|
|
||||||
onInput,
|
|
||||||
onKeydown,
|
|
||||||
updated,
|
|
||||||
i18n,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -9,49 +9,41 @@
|
||||||
</Sortable>
|
</Sortable>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent, defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import XSection from './els/page-editor.el.section.vue';
|
import XSection from './els/page-editor.el.section.vue';
|
||||||
import XText from './els/page-editor.el.text.vue';
|
import XText from './els/page-editor.el.text.vue';
|
||||||
import XImage from './els/page-editor.el.image.vue';
|
import XImage from './els/page-editor.el.image.vue';
|
||||||
import XNote from './els/page-editor.el.note.vue';
|
import XNote from './els/page-editor.el.note.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||||
components: {
|
|
||||||
Sortable: defineAsyncComponent(() => import('vuedraggable').then(x => x.default)),
|
|
||||||
XSection, XText, XImage, XNote,
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
const props = defineProps<{
|
||||||
modelValue: {
|
modelValue: any[];
|
||||||
type: Array,
|
}>();
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['update:modelValue'],
|
const emit = defineEmits<{
|
||||||
|
(ev: 'update:modelValue', value: any[]): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
methods: {
|
function updateItem(v) {
|
||||||
updateItem(v) {
|
const i = props.modelValue.findIndex(x => x.id === v.id);
|
||||||
const i = this.modelValue.findIndex(x => x.id === v.id);
|
|
||||||
const newValue = [
|
const newValue = [
|
||||||
...this.modelValue.slice(0, i),
|
...props.modelValue.slice(0, i),
|
||||||
v,
|
v,
|
||||||
...this.modelValue.slice(i + 1),
|
...props.modelValue.slice(i + 1),
|
||||||
];
|
];
|
||||||
this.$emit('update:modelValue', newValue);
|
emit('update:modelValue', newValue);
|
||||||
},
|
}
|
||||||
|
|
||||||
removeItem(el) {
|
function removeItem(el) {
|
||||||
const i = this.modelValue.findIndex(x => x.id === el.id);
|
const i = props.modelValue.findIndex(x => x.id === el.id);
|
||||||
const newValue = [
|
const newValue = [
|
||||||
...this.modelValue.slice(0, i),
|
...props.modelValue.slice(0, i),
|
||||||
...this.modelValue.slice(i + 1),
|
...props.modelValue.slice(i + 1),
|
||||||
];
|
];
|
||||||
this.$emit('update:modelValue', newValue);
|
emit('update:modelValue', newValue);
|
||||||
},
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="cpjygsrt" :class="{ error: error != null, warn: warn != null }">
|
<div class="cpjygsrt">
|
||||||
<header>
|
<header>
|
||||||
<div class="title"><slot name="header"></slot></div>
|
<div class="title"><slot name="header"></slot></div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
@ -16,58 +16,40 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<p v-show="showBody" v-if="error != null" class="error">{{ i18n.t('_pages.script.typeError', { slot: error.arg + 1, expect: i18n.t(`script.types.${error.expect}`), actual: i18n.t(`script.types.${error.actual}`) }) }}</p>
|
|
||||||
<p v-show="showBody" v-if="warn != null" class="warn">{{ i18n.t('_pages.script.thereIsEmptySlot', { slot: warn.slot + 1 }) }}</p>
|
|
||||||
<div v-show="showBody" class="body">
|
<div v-show="showBody" class="body">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
export default defineComponent({
|
const props = withDefaults(defineProps<{
|
||||||
props: {
|
expanded?: boolean;
|
||||||
expanded: {
|
removable?: boolean;
|
||||||
type: Boolean,
|
draggable?: boolean;
|
||||||
default: true,
|
}>(), {
|
||||||
},
|
expanded: true,
|
||||||
removable: {
|
removable: true,
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
draggable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
warn: {
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['toggle', 'remove'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showBody: this.expanded,
|
|
||||||
i18n,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleContent(show: boolean) {
|
|
||||||
this.showBody = show;
|
|
||||||
this.$emit('toggle', show);
|
|
||||||
},
|
|
||||||
remove() {
|
|
||||||
this.$emit('remove');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'toggle', show: boolean): void;
|
||||||
|
(ev: 'remove'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const showBody = ref(props.expanded);
|
||||||
|
|
||||||
|
function toggleContent(show: boolean) {
|
||||||
|
showBody.value = show;
|
||||||
|
emit('toggle', show);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove() {
|
||||||
|
emit('remove');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -128,20 +110,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .warn {
|
|
||||||
color: #b19e49;
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px 16px 0 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .error {
|
|
||||||
color: #f00;
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px 16px 0 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .body {
|
> .body {
|
||||||
::v-deep(.juejbjww), ::v-deep(.eiipwacr) {
|
::v-deep(.juejbjww), ::v-deep(.eiipwacr) {
|
||||||
&:not(.inline):first-child {
|
&:not(.inline):first-child {
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<MkSample/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import MkSample from '@/components/MkSample.vue';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
|
||||||
title: i18n.ts.preview,
|
|
||||||
icon: 'ti ti-eye',
|
|
||||||
})));
|
|
||||||
</script>
|
|
|
@ -242,9 +242,6 @@ export const routes = [{
|
||||||
}, {
|
}, {
|
||||||
path: '/scratchpad',
|
path: '/scratchpad',
|
||||||
component: page(() => import('./pages/scratchpad.vue')),
|
component: page(() => import('./pages/scratchpad.vue')),
|
||||||
}, {
|
|
||||||
path: '/preview',
|
|
||||||
component: page(() => import('./pages/preview.vue')),
|
|
||||||
}, {
|
}, {
|
||||||
path: '/auth/:token',
|
path: '/auth/:token',
|
||||||
component: page(() => import('./pages/auth.vue')),
|
component: page(() => import('./pages/auth.vue')),
|
||||||
|
|
Loading…
Reference in a new issue