Improve MisskeyPages

This commit is contained in:
syuilo 2019-05-02 17:55:59 +09:00
parent 2fdec27ab0
commit 2d1f50303d
No known key found for this signature in database
GPG key ID: BDC4C49D06AB9D69
21 changed files with 248 additions and 176 deletions

View file

@ -1915,6 +1915,12 @@ pages:
text: "タイトル" text: "タイトル"
default: "デフォルト値" default: "デフォルト値"
counter: "カウンター"
_counter:
name: "変数名"
text: "タイトル"
inc: "増加値"
_button: _button:
text: "タイトル" text: "タイトル"
action: "ボタンを押したときの動作" action: "ボタンを押したときの動作"

View file

@ -26,6 +26,12 @@ export function collectPageVars(content) {
type: 'boolean', type: 'boolean',
value: x.default || false value: x.default || false
}); });
} else if (x.type === 'counter') {
pageVars.push({
name: x.name,
type: 'number',
value: 0
});
} else if (x.children) { } else if (x.children) {
collect(x.children); collect(x.children);
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.button') }}</template> <template #header><fa :icon="faBolt"/> {{ $t('blocks.button') }}</template>
<section class="xfhsjczc"> <section class="xfhsjczc">

View file

@ -0,0 +1,42 @@
<template>
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.counter') }}</template>
<section style="padding: 0 16px 0 16px;">
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._counter.name') }}</span></ui-input>
<ui-input v-model="value.text"><span>{{ $t('blocks._counter.text') }}</span></ui-input>
<ui-input v-model="value.inc" type="number"><span>{{ $t('blocks._counter.increment') }}</span></ui-input>
</section>
</x-container>
</template>
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../../../../i18n';
import XContainer from '../page-editor.container.vue';
export default Vue.extend({
i18n: i18n('pages'),
components: {
XContainer
},
props: {
value: {
required: true
},
},
data() {
return {
faBolt, faMagic
};
},
created() {
if (this.value.name == null) Vue.set(this.value, 'name', '');
},
});
</script>

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faQuestion"/> {{ $t('blocks.if') }}</template> <template #header><fa :icon="faQuestion"/> {{ $t('blocks.if') }}</template>
<template #func> <template #func>
<button @click="add()"> <button @click="add()">
@ -19,9 +19,7 @@
</optgroup> </optgroup>
</ui-select> </ui-select>
<div class="children"> <x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
<x-block v-for="child in value.children" :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id" :ai-script="aiScript"/>
</div>
</section> </section>
</x-container> </x-container>
</template> </template>
@ -58,7 +56,7 @@ export default Vue.extend({
}, },
beforeCreate() { beforeCreate() {
this.$options.components.XBlock = require('../page-editor.block.vue').default this.$options.components.XBlocks = require('../page-editor.blocks.vue').default
}, },
created() { created() {
@ -81,27 +79,6 @@ export default Vue.extend({
const id = uuid.v4(); const id = uuid.v4();
this.value.children.push({ id, type }); this.value.children.push({ id, type });
}, },
updateItem(v) {
const i = this.value.children.findIndex(x => x.id === v.id);
const newValue = [
...this.value.children.slice(0, i),
v,
...this.value.children.slice(i + 1)
];
this.value.children = newValue;
this.$emit('input', this.value);
},
remove(el) {
const i = this.value.children.findIndex(x => x.id === el.id);
const newValue = [
...this.value.children.slice(0, i),
...this.value.children.slice(i + 1)
];
this.value.children = newValue;
this.$emit('input', this.value);
}
} }
}); });
</script> </script>

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faImage"/> {{ $t('blocks.image') }}</template> <template #header><fa :icon="faImage"/> {{ $t('blocks.image') }}</template>
<template #func> <template #func>
<button @click="choose()"> <button @click="choose()">

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.numberInput') }}</template> <template #header><fa :icon="faBolt"/> {{ $t('blocks.numberInput') }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faPaperPlane"/> {{ $t('blocks.post') }}</template> <template #header><fa :icon="faPaperPlane"/> {{ $t('blocks.post') }}</template>
<section style="padding: 0 16px 16px 16px;"> <section style="padding: 0 16px 16px 16px;">
@ -33,10 +33,6 @@ export default Vue.extend({
}; };
}, },
beforeCreate() {
this.$options.components.XBlock = require('../page-editor.block.vue').default
},
created() { created() {
if (this.value.text == null) Vue.set(this.value, 'text', ''); if (this.value.text == null) Vue.set(this.value, 'text', '');
}, },

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faStickyNote"/> {{ value.title }}</template> <template #header><fa :icon="faStickyNote"/> {{ value.title }}</template>
<template #func> <template #func>
<button @click="rename()"> <button @click="rename()">
@ -11,9 +11,7 @@
</template> </template>
<section class="ilrvjyvi"> <section class="ilrvjyvi">
<div class="children"> <x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
<x-block v-for="child in value.children" :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id" :ai-script="aiScript"/>
</div>
</section> </section>
</x-container> </x-container>
</template> </template>
@ -51,7 +49,7 @@ export default Vue.extend({
}, },
beforeCreate() { beforeCreate() {
this.$options.components.XBlock = require('../page-editor.block.vue').default this.$options.components.XBlocks = require('../page-editor.blocks.vue').default
}, },
created() { created() {
@ -93,27 +91,6 @@ export default Vue.extend({
const id = uuid.v4(); const id = uuid.v4();
this.value.children.push({ id, type }); this.value.children.push({ id, type });
}, },
updateItem(v) {
const i = this.value.children.findIndex(x => x.id === v.id);
const newValue = [
...this.value.children.slice(0, i),
v,
...this.value.children.slice(i + 1)
];
this.value.children = newValue;
this.$emit('input', this.value);
},
remove(el) {
const i = this.value.children.findIndex(x => x.id === el.id);
const newValue = [
...this.value.children.slice(0, i),
...this.value.children.slice(i + 1)
];
this.value.children = newValue;
this.$emit('input', this.value);
}
} }
}); });
</script> </script>

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.switch') }}</template> <template #header><fa :icon="faBolt"/> {{ $t('blocks.switch') }}</template>
<section class="kjuadyyj"> <section class="kjuadyyj">

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.textInput') }}</template> <template #header><fa :icon="faBolt"/> {{ $t('blocks.textInput') }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.text') }}</template> <template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.text') }}</template>
<section class="ihymsbbe"> <section class="ihymsbbe">

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.textareaInput') }}</template> <template #header><fa :icon="faBolt"/> {{ $t('blocks.textareaInput') }}</template>
<section style="padding: 0 16px 16px 16px;"> <section style="padding: 0 16px 16px 16px;">

View file

@ -1,5 +1,5 @@
<template> <template>
<x-container @remove="() => $emit('remove')"> <x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.textarea') }}</template> <template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.textarea') }}</template>
<section class="ihymsbbe"> <section class="ihymsbbe">

View file

@ -1,33 +0,0 @@
<template>
<component :is="'x-' + value.type" :value="value" @input="v => updateItem(v)" @remove="() => $emit('remove', value)" :key="value.id" :ai-script="aiScript"/>
</template>
<script lang="ts">
import Vue from 'vue';
import XSection from './els/page-editor.el.section.vue';
import XText from './els/page-editor.el.text.vue';
import XTextarea from './els/page-editor.el.textarea.vue';
import XImage from './els/page-editor.el.image.vue';
import XButton from './els/page-editor.el.button.vue';
import XTextInput from './els/page-editor.el.text-input.vue';
import XTextareaInput from './els/page-editor.el.textarea-input.vue';
import XNumberInput from './els/page-editor.el.text-input.vue';
import XSwitch from './els/page-editor.el.switch.vue';
import XIf from './els/page-editor.el.if.vue';
import XPost from './els/page-editor.el.post.vue';
export default Vue.extend({
components: {
XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost
},
props: {
value: {
required: true
},
aiScript: {
required: true,
},
},
});
</script>

View file

@ -0,0 +1,65 @@
<template>
<x-draggable tag="div" :list="blocks" handle=".drag-handle" :group="{ name: 'blocks' }" animation="150">
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="removeItem" :key="block.id" :ai-script="aiScript"/>
</x-draggable>
</template>
<script lang="ts">
import Vue from 'vue';
import * as XDraggable from 'vuedraggable';
import XSection from './els/page-editor.el.section.vue';
import XText from './els/page-editor.el.text.vue';
import XTextarea from './els/page-editor.el.textarea.vue';
import XImage from './els/page-editor.el.image.vue';
import XButton from './els/page-editor.el.button.vue';
import XTextInput from './els/page-editor.el.text-input.vue';
import XTextareaInput from './els/page-editor.el.textarea-input.vue';
import XNumberInput from './els/page-editor.el.text-input.vue';
import XSwitch from './els/page-editor.el.switch.vue';
import XIf from './els/page-editor.el.if.vue';
import XPost from './els/page-editor.el.post.vue';
import XCounter from './els/page-editor.el.counter.vue';
export default Vue.extend({
components: {
XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter
},
props: {
value: {
type: Array,
required: true
},
aiScript: {
required: true,
},
},
computed: {
blocks() {
return this.value;
}
},
methods: {
updateItem(v) {
const i = this.blocks.findIndex(x => x.id === v.id);
const newValue = [
...this.blocks.slice(0, i),
v,
...this.blocks.slice(i + 1)
];
this.$emit('input', newValue);
},
removeItem(el) {
const i = this.blocks.findIndex(x => x.id === el.id);
const newValue = [
...this.blocks.slice(0, i),
...this.blocks.slice(i + 1)
];
this.$emit('input', newValue);
},
}
});
</script>

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="cpjygsrt" :class="{ error: error != null, warn: warn != null }"> <div class="cpjygsrt" :class="{ error: error != null, warn: warn != null, draggable }">
<header> <header class="drag-handle">
<div class="title"><slot name="header"></slot></div> <div class="title"><slot name="header"></slot></div>
<div class="buttons"> <div class="buttons">
<slot name="func"></slot> <slot name="func"></slot>
@ -38,6 +38,10 @@ export default Vue.extend({
type: Boolean, type: Boolean,
default: true default: true
}, },
draggable: {
type: Boolean,
default: false
},
error: { error: {
required: false, required: false,
default: null default: null
@ -120,6 +124,10 @@ export default Vue.extend({
&:active &:active
color var(--faceTextButtonActive) color var(--faceTextButtonActive)
&.draggable
> header
cursor move
> .warn > .warn
color #b19e49 color #b19e49
margin 0 margin 0

View file

@ -44,9 +44,7 @@
</div> </div>
</template> </template>
<div class="content" v-for="child in content"> <x-blocks class="content" v-model="content" :ai-script="aiScript"/>
<x-block :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id" :ai-script="aiScript"/>
</div>
<ui-button @click="add()" v-if="!readonly"><fa :icon="faPlus"/></ui-button> <ui-button @click="add()" v-if="!readonly"><fa :icon="faPlus"/></ui-button>
</section> </section>
@ -98,7 +96,7 @@ import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } fr
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../../../i18n'; import i18n from '../../../../i18n';
import XVariable from './page-editor.script-block.vue'; import XVariable from './page-editor.script-block.vue';
import XBlock from './page-editor.block.vue'; import XBlocks from './page-editor.blocks.vue';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import { blockDefs } from '../../../../../../misc/aiscript/index'; import { blockDefs } from '../../../../../../misc/aiscript/index';
import { ASTypeChecker } from '../../../../../../misc/aiscript/type-checker'; import { ASTypeChecker } from '../../../../../../misc/aiscript/type-checker';
@ -109,7 +107,7 @@ export default Vue.extend({
i18n: i18n('pages'), i18n: i18n('pages'),
components: { components: {
XVariable, XBlock XVariable, XBlocks
}, },
props: { props: {
@ -299,25 +297,6 @@ export default Vue.extend({
this.variables.push({ id, name, type: null }); this.variables.push({ id, name, type: null });
}, },
updateItem(v) {
const i = this.content.findIndex(x => x.id === v.id);
const newValue = [
...this.content.slice(0, i),
v,
...this.content.slice(i + 1)
];
this.content = newValue;
},
remove(el) {
const i = this.content.findIndex(x => x.id === el.id);
const newValue = [
...this.content.slice(0, i),
...this.content.slice(i + 1)
];
this.content = newValue;
},
removeVariable(v) { removeVariable(v) {
const i = this.variables.findIndex(x => x.name === v.name); const i = this.variables.findIndex(x => x.name === v.name);
const newValue = [ const newValue = [
@ -343,7 +322,8 @@ export default Vue.extend({
{ value: 'textInput', text: this.$t('blocks.textInput') }, { value: 'textInput', text: this.$t('blocks.textInput') },
{ value: 'textareaInput', text: this.$t('blocks.textareaInput') }, { value: 'textareaInput', text: this.$t('blocks.textareaInput') },
{ value: 'numberInput', text: this.$t('blocks.numberInput') }, { value: 'numberInput', text: this.$t('blocks.numberInput') },
{ value: 'switch', text: this.$t('blocks.switch') } { value: 'switch', text: this.$t('blocks.switch') },
{ value: 'counter', text: this.$t('blocks.counter') }
] ]
}, { }, {
label: this.$t('special-blocks'), label: this.$t('special-blocks'),

View file

@ -15,10 +15,11 @@ import XSwitch from './page.switch.vue';
import XIf from './page.if.vue'; import XIf from './page.if.vue';
import XTextarea from './page.textarea.vue'; import XTextarea from './page.textarea.vue';
import XPost from './page.post.vue'; import XPost from './page.post.vue';
import XCounter from './page.counter.vue';
export default Vue.extend({ export default Vue.extend({
components: { components: {
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter
}, },
props: { props: {

View file

@ -0,0 +1,47 @@
<template>
<div>
<ui-button class="llumlmnx" @click="click()">{{ script.interpolate(value.text) }}</ui-button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
value: {
required: true
},
script: {
required: true
}
},
data() {
return {
v: 0,
};
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
},
methods: {
click() {
this.v = this.v + (this.value.inc || 1);
}
}
});
</script>
<style lang="stylus" scoped>
.llumlmnx
display inline-block
min-width 300px
max-width 450px
margin 8px 0
</style>

View file

@ -7,56 +7,6 @@ type Fn = {
exec: (args: Record<string, any>) => ReturnType<ASEvaluator['evaluate']>; exec: (args: Record<string, any>) => ReturnType<ASEvaluator['evaluate']>;
}; };
class AiScriptError extends Error {
public info?: any;
constructor(message: string, info?: any) {
super(message);
this.info = info;
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, AiScriptError);
}
}
}
class Scope {
private layerdStates: Record<string, any>[];
public name: string;
constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) {
this.layerdStates = layerdStates;
this.name = name || 'anonymous';
}
@autobind
public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope {
const layer = [states, ...this.layerdStates];
return new Scope(layer, name);
}
/**
*
* @param name
*/
@autobind
public getState(name: string): any {
for (const later of this.layerdStates) {
const state = later[name];
if (state !== undefined) {
return state;
}
}
throw new AiScriptError(
`No such variable '${name}' in scope '${this.name}'`, {
scope: this.layerdStates
});
}
}
/** /**
* AiScript evaluator * AiScript evaluator
*/ */
@ -238,3 +188,53 @@ export class ASEvaluator {
} }
} }
} }
class AiScriptError extends Error {
public info?: any;
constructor(message: string, info?: any) {
super(message);
this.info = info;
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, AiScriptError);
}
}
}
class Scope {
private layerdStates: Record<string, any>[];
public name: string;
constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) {
this.layerdStates = layerdStates;
this.name = name || 'anonymous';
}
@autobind
public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope {
const layer = [states, ...this.layerdStates];
return new Scope(layer, name);
}
/**
*
* @param name
*/
@autobind
public getState(name: string): any {
for (const later of this.layerdStates) {
const state = later[name];
if (state !== undefined) {
return state;
}
}
throw new AiScriptError(
`No such variable '${name}' in scope '${this.name}'`, {
scope: this.layerdStates
});
}
}