rename: client -> frontend
This commit is contained in:
		
							
								
								
									
										44
									
								
								packages/frontend/src/components/page/page.block.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								packages/frontend/src/components/page/page.block.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
<template>
 | 
			
		||||
<component :is="'x-' + block.type" :key="block.id" :block="block" :hpml="hpml" :h="h"/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, PropType } from 'vue';
 | 
			
		||||
import XText from './page.text.vue';
 | 
			
		||||
import XSection from './page.section.vue';
 | 
			
		||||
import XImage from './page.image.vue';
 | 
			
		||||
import XButton from './page.button.vue';
 | 
			
		||||
import XNumberInput from './page.number-input.vue';
 | 
			
		||||
import XTextInput from './page.text-input.vue';
 | 
			
		||||
import XTextareaInput from './page.textarea-input.vue';
 | 
			
		||||
import XSwitch from './page.switch.vue';
 | 
			
		||||
import XIf from './page.if.vue';
 | 
			
		||||
import XTextarea from './page.textarea.vue';
 | 
			
		||||
import XPost from './page.post.vue';
 | 
			
		||||
import XCounter from './page.counter.vue';
 | 
			
		||||
import XRadioButton from './page.radio-button.vue';
 | 
			
		||||
import XCanvas from './page.canvas.vue';
 | 
			
		||||
import XNote from './page.note.vue';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { Block } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas, XNote,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<Block>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		h: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										66
									
								
								packages/frontend/src/components/page/page.button.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								packages/frontend/src/components/page/page.button.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<MkButton class="kudkigyw" :primary="block.primary" @click="click()">{{ hpml.interpolate(block.text) }}</MkButton>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, PropType, unref } from 'vue';
 | 
			
		||||
import MkButton from '../MkButton.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { ButtonBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkButton,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<ButtonBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		click() {
 | 
			
		||||
			if (this.block.action === 'dialog') {
 | 
			
		||||
				this.hpml.eval();
 | 
			
		||||
				os.alert({
 | 
			
		||||
					text: this.hpml.interpolate(this.block.content),
 | 
			
		||||
				});
 | 
			
		||||
			} else if (this.block.action === 'resetRandom') {
 | 
			
		||||
				this.hpml.updateRandomSeed(Math.random());
 | 
			
		||||
				this.hpml.eval();
 | 
			
		||||
			} else if (this.block.action === 'pushEvent') {
 | 
			
		||||
				os.api('page-push', {
 | 
			
		||||
					pageId: this.hpml.page.id,
 | 
			
		||||
					event: this.block.event,
 | 
			
		||||
					...(this.block.var ? {
 | 
			
		||||
						var: unref(this.hpml.vars)[this.block.var],
 | 
			
		||||
					} : {}),
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				os.alert({
 | 
			
		||||
					type: 'success',
 | 
			
		||||
					text: this.hpml.interpolate(this.block.message),
 | 
			
		||||
				});
 | 
			
		||||
			} else if (this.block.action === 'callAiScript') {
 | 
			
		||||
				this.hpml.callAiScript(this.block.fn);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.kudkigyw {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	min-width: 200px;
 | 
			
		||||
	max-width: 450px;
 | 
			
		||||
	margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										49
									
								
								packages/frontend/src/components/page/page.canvas.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								packages/frontend/src/components/page/page.canvas.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ysrxegms">
 | 
			
		||||
	<canvas ref="canvas" :width="block.width" :height="block.height"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, onMounted, PropType, Ref, ref } from 'vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { CanvasBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<CanvasBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const canvas: Ref<any> = ref(null);
 | 
			
		||||
 | 
			
		||||
		onMounted(() => {
 | 
			
		||||
			props.hpml.registerCanvas(props.block.name, canvas.value);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			canvas,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.ysrxegms {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	vertical-align: bottom;
 | 
			
		||||
	overflow: auto;
 | 
			
		||||
	max-width: 100%;
 | 
			
		||||
 | 
			
		||||
	> canvas {
 | 
			
		||||
		display: block;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										52
									
								
								packages/frontend/src/components/page/page.counter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								packages/frontend/src/components/page/page.counter.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<MkButton class="llumlmnx" @click="click()">{{ hpml.interpolate(block.text) }}</MkButton>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkButton from '../MkButton.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { CounterVarBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkButton,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<CounterVarBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const value = computed(() => {
 | 
			
		||||
			return props.hpml.vars.value[props.block.name];
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		function click() {
 | 
			
		||||
			props.hpml.updatePageVar(props.block.name, value.value + (props.block.inc || 1));
 | 
			
		||||
			props.hpml.eval();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			click,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.llumlmnx {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	min-width: 300px;
 | 
			
		||||
	max-width: 450px;
 | 
			
		||||
	margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										31
									
								
								packages/frontend/src/components/page/page.if.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								packages/frontend/src/components/page/page.if.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div v-show="hpml.vars.value[block.var]">
 | 
			
		||||
	<XBlock v-for="child in block.children" :key="child.id" :block="child" :hpml="hpml" :h="h"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { IfBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { defineComponent, defineAsyncComponent, PropType } from 'vue';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XBlock: defineAsyncComponent(() => import('./page.block.vue')),
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<IfBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		h: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										28
									
								
								packages/frontend/src/components/page/page.image.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								packages/frontend/src/components/page/page.image.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="lzyxtsnt">
 | 
			
		||||
	<ImgWithBlurhash v-if="image" :hash="image.blurhash" :src="image.url" :alt="image.comment" :title="image.comment" :cover="false"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { defineComponent, PropType } from 'vue';
 | 
			
		||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { ImageBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	block: PropType<ImageBlock>,
 | 
			
		||||
	hpml: PropType<Hpml>,
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.lzyxtsnt {
 | 
			
		||||
	> img {
 | 
			
		||||
		max-width: 100%;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										47
									
								
								packages/frontend/src/components/page/page.note.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								packages/frontend/src/components/page/page.note.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="voxdxuby">
 | 
			
		||||
	<XNote v-if="note && !block.detailed" :key="note.id + ':normal'" v-model:note="note"/>
 | 
			
		||||
	<XNoteDetailed v-if="note && block.detailed" :key="note.id + ':detail'" v-model:note="note"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, onMounted, PropType, Ref, ref } from 'vue';
 | 
			
		||||
import XNote from '@/components/MkNote.vue';
 | 
			
		||||
import XNoteDetailed from '@/components/MkNoteDetailed.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { NoteBlock } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XNote,
 | 
			
		||||
		XNoteDetailed,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<NoteBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const note: Ref<Record<string, any> | null> = ref(null);
 | 
			
		||||
 | 
			
		||||
		onMounted(() => {
 | 
			
		||||
			os.api('notes/show', { noteId: props.block.note })
 | 
			
		||||
			.then(result => {
 | 
			
		||||
				note.value = result;
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			note,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.voxdxuby {
 | 
			
		||||
	margin: 1em 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										55
									
								
								packages/frontend/src/components/page/page.number-input.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								packages/frontend/src/components/page/page.number-input.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<MkInput class="kudkigyw" :model-value="value" type="number" @update:model-value="updateValue($event)">
 | 
			
		||||
		<template #label>{{ hpml.interpolate(block.text) }}</template>
 | 
			
		||||
	</MkInput>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkInput from '../form/input.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { NumberInputVarBlock } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkInput,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<NumberInputVarBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const value = computed(() => {
 | 
			
		||||
			return props.hpml.vars.value[props.block.name];
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		function updateValue(newValue) {
 | 
			
		||||
			props.hpml.updatePageVar(props.block.name, newValue);
 | 
			
		||||
			props.hpml.eval();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.kudkigyw {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	min-width: 300px;
 | 
			
		||||
	max-width: 450px;
 | 
			
		||||
	margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										109
									
								
								packages/frontend/src/components/page/page.post.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								packages/frontend/src/components/page/page.post.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ngbfujlo">
 | 
			
		||||
	<MkTextarea :model-value="text" readonly style="margin: 0;"></MkTextarea>
 | 
			
		||||
	<MkButton class="button" primary :disabled="posting || posted" @click="post()">
 | 
			
		||||
		<i v-if="posted" class="ti ti-check"></i>
 | 
			
		||||
		<i v-else class="ti ti-send"></i>
 | 
			
		||||
	</MkButton>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkTextarea from '../form/textarea.vue';
 | 
			
		||||
import MkButton from '../MkButton.vue';
 | 
			
		||||
import { apiUrl } from '@/config';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { PostBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkTextarea,
 | 
			
		||||
		MkButton,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<PostBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			text: this.hpml.interpolate(this.block.text),
 | 
			
		||||
			posted: false,
 | 
			
		||||
			posting: false,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		'hpml.vars': {
 | 
			
		||||
			handler() {
 | 
			
		||||
				this.text = this.hpml.interpolate(this.block.text);
 | 
			
		||||
			},
 | 
			
		||||
			deep: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		upload() {
 | 
			
		||||
			const promise = new Promise((ok) => {
 | 
			
		||||
				const canvas = this.hpml.canvases[this.block.canvasId];
 | 
			
		||||
				canvas.toBlob(blob => {
 | 
			
		||||
					const formData = new FormData();
 | 
			
		||||
					formData.append('file', blob);
 | 
			
		||||
					formData.append('i', this.$i.token);
 | 
			
		||||
					if (this.$store.state.uploadFolder) {
 | 
			
		||||
						formData.append('folderId', this.$store.state.uploadFolder);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					window.fetch(apiUrl + '/drive/files/create', {
 | 
			
		||||
						method: 'POST',
 | 
			
		||||
						body: formData,
 | 
			
		||||
					})
 | 
			
		||||
						.then(response => response.json())
 | 
			
		||||
						.then(f => {
 | 
			
		||||
							ok(f);
 | 
			
		||||
						});
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
			os.promiseDialog(promise);
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
		async post() {
 | 
			
		||||
			this.posting = true;
 | 
			
		||||
			const file = this.block.attachCanvasImage ? await this.upload() : null;
 | 
			
		||||
			os.apiWithDialog('notes/create', {
 | 
			
		||||
				text: this.text === '' ? null : this.text,
 | 
			
		||||
				fileIds: file ? [file.id] : undefined,
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.posted = true;
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.ngbfujlo {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	padding: 32px;
 | 
			
		||||
	border-radius: 6px;
 | 
			
		||||
	box-shadow: 0 2px 8px var(--shadow);
 | 
			
		||||
	z-index: 1;
 | 
			
		||||
 | 
			
		||||
	> .button {
 | 
			
		||||
		margin-top: 32px;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@media (max-width: 600px) {
 | 
			
		||||
		padding: 16px;
 | 
			
		||||
 | 
			
		||||
		> .button {
 | 
			
		||||
			margin-top: 16px;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										45
									
								
								packages/frontend/src/components/page/page.radio-button.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								packages/frontend/src/components/page/page.radio-button.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<div>{{ hpml.interpolate(block.title) }}</div>
 | 
			
		||||
	<MkRadio v-for="item in block.values" :key="item" :modelValue="value" :value="item" @update:model-value="updateValue($event)">{{ item }}</MkRadio>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkRadio from '../form/radio.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { RadioButtonVarBlock } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkRadio,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<RadioButtonVarBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const value = computed(() => {
 | 
			
		||||
			return props.hpml.vars.value[props.block.name];
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		function updateValue(newValue: string) {
 | 
			
		||||
			props.hpml.updatePageVar(props.block.name, newValue);
 | 
			
		||||
			props.hpml.eval();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										60
									
								
								packages/frontend/src/components/page/page.section.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								packages/frontend/src/components/page/page.section.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
<template>
 | 
			
		||||
<section class="sdgxphyu">
 | 
			
		||||
	<component :is="'h' + h">{{ block.title }}</component>
 | 
			
		||||
 | 
			
		||||
	<div class="children">
 | 
			
		||||
		<XBlock v-for="child in block.children" :key="child.id" :block="child" :hpml="hpml" :h="h + 1"/>
 | 
			
		||||
	</div>
 | 
			
		||||
</section>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, defineAsyncComponent, PropType } from 'vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { SectionBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XBlock: defineAsyncComponent(() => import('./page.block.vue')),
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<SectionBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		h: {
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.sdgxphyu {
 | 
			
		||||
	margin: 1.5em 0;
 | 
			
		||||
 | 
			
		||||
	> h2 {
 | 
			
		||||
		font-size: 1.35em;
 | 
			
		||||
		margin: 0 0 0.5em 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> h3 {
 | 
			
		||||
		font-size: 1em;
 | 
			
		||||
		margin: 0 0 0.5em 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> h4 {
 | 
			
		||||
		font-size: 1em;
 | 
			
		||||
		margin: 0 0 0.5em 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> .children {
 | 
			
		||||
		//padding 16px
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										55
									
								
								packages/frontend/src/components/page/page.switch.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								packages/frontend/src/components/page/page.switch.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="hkcxmtwj">
 | 
			
		||||
	<MkSwitch :model-value="value" @update:model-value="updateValue($event)">{{ hpml.interpolate(block.text) }}</MkSwitch>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkSwitch from '../form/switch.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { SwitchVarBlock } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkSwitch,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<SwitchVarBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const value = computed(() => {
 | 
			
		||||
			return props.hpml.vars.value[props.block.name];
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		function updateValue(newValue: boolean) {
 | 
			
		||||
			props.hpml.updatePageVar(props.block.name, newValue);
 | 
			
		||||
			props.hpml.eval();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.hkcxmtwj {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	margin: 16px auto;
 | 
			
		||||
 | 
			
		||||
	& + .hkcxmtwj {
 | 
			
		||||
		margin-left: 16px;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										55
									
								
								packages/frontend/src/components/page/page.text-input.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								packages/frontend/src/components/page/page.text-input.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<MkInput class="kudkigyw" :model-value="value" type="text" @update:model-value="updateValue($event)">
 | 
			
		||||
		<template #label>{{ hpml.interpolate(block.text) }}</template>
 | 
			
		||||
	</MkInput>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkInput from '../form/input.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { TextInputVarBlock } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkInput,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<TextInputVarBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const value = computed(() => {
 | 
			
		||||
			return props.hpml.vars.value[props.block.name];
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		function updateValue(newValue) {
 | 
			
		||||
			props.hpml.updatePageVar(props.block.name, newValue);
 | 
			
		||||
			props.hpml.eval();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.kudkigyw {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	min-width: 300px;
 | 
			
		||||
	max-width: 450px;
 | 
			
		||||
	margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										68
									
								
								packages/frontend/src/components/page/page.text.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								packages/frontend/src/components/page/page.text.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mrdgzndn">
 | 
			
		||||
	<Mfm :key="text" :text="text" :is-note="false" :i="$i"/>
 | 
			
		||||
	<MkUrlPreview v-for="url in urls" :key="url" :url="url" class="url"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { TextBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { defineAsyncComponent, defineComponent, PropType } from 'vue';
 | 
			
		||||
import * as mfm from 'mfm-js';
 | 
			
		||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkUrlPreview: defineAsyncComponent(() => import('@/components/MkUrlPreview.vue')),
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<TextBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			text: this.hpml.interpolate(this.block.text),
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		urls(): string[] {
 | 
			
		||||
			if (this.text) {
 | 
			
		||||
				return extractUrlFromMfm(mfm.parse(this.text));
 | 
			
		||||
			} else {
 | 
			
		||||
				return [];
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		'hpml.vars': {
 | 
			
		||||
			handler() {
 | 
			
		||||
				this.text = this.hpml.interpolate(this.block.text);
 | 
			
		||||
			},
 | 
			
		||||
			deep: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.mrdgzndn {
 | 
			
		||||
	&:not(:first-child) {
 | 
			
		||||
		margin-top: 0.5em;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&:not(:last-child) {
 | 
			
		||||
		margin-bottom: 0.5em;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> .url {
 | 
			
		||||
		margin: 0.5em 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -0,0 +1,47 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<MkTextarea :model-value="value" @update:model-value="updateValue($event)">
 | 
			
		||||
		<template #label>{{ hpml.interpolate(block.text) }}</template>
 | 
			
		||||
	</MkTextarea>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkTextarea from '../form/textarea.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { HpmlTextInput } from '@/scripts/hpml';
 | 
			
		||||
import { TextInputVarBlock } from '@/scripts/hpml/block';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkTextarea,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<TextInputVarBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const value = computed(() => {
 | 
			
		||||
			return props.hpml.vars.value[props.block.name];
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		function updateValue(newValue) {
 | 
			
		||||
			props.hpml.updatePageVar(props.block.name, newValue);
 | 
			
		||||
			props.hpml.eval();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			value,
 | 
			
		||||
			updateValue,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										39
									
								
								packages/frontend/src/components/page/page.textarea.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								packages/frontend/src/components/page/page.textarea.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<template>
 | 
			
		||||
<MkTextarea :model-value="text" readonly></MkTextarea>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { TextBlock } from '@/scripts/hpml/block';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { defineComponent, PropType } from 'vue';
 | 
			
		||||
import MkTextarea from '../form/textarea.vue';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		MkTextarea,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		block: {
 | 
			
		||||
			type: Object as PropType<TextBlock>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		hpml: {
 | 
			
		||||
			type: Object as PropType<Hpml>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			text: this.hpml.interpolate(this.block.text),
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		'hpml.vars': {
 | 
			
		||||
			handler() {
 | 
			
		||||
				this.text = this.hpml.interpolate(this.block.text);
 | 
			
		||||
			},
 | 
			
		||||
			deep: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										85
									
								
								packages/frontend/src/components/page/page.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								packages/frontend/src/components/page/page.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div v-if="hpml" class="iroscrza" :class="{ center: page.alignCenter, serif: page.font === 'serif' }">
 | 
			
		||||
	<XBlock v-for="child in page.content" :key="child.id" :block="child" :hpml="hpml" :h="2"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, onMounted, nextTick, onUnmounted, PropType } from 'vue';
 | 
			
		||||
import { parse } from '@syuilo/aiscript';
 | 
			
		||||
import XBlock from './page.block.vue';
 | 
			
		||||
import { Hpml } from '@/scripts/hpml/evaluator';
 | 
			
		||||
import { url } from '@/config';
 | 
			
		||||
import { $i } from '@/account';
 | 
			
		||||
import { defaultStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XBlock,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		page: {
 | 
			
		||||
			type: Object as PropType<Record<string, any>>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props, ctx) {
 | 
			
		||||
		const hpml = new Hpml(props.page, {
 | 
			
		||||
			randomSeed: Math.random(),
 | 
			
		||||
			visitor: $i,
 | 
			
		||||
			url: url,
 | 
			
		||||
			enableAiScript: !defaultStore.state.disablePagesScript,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		onMounted(() => {
 | 
			
		||||
			nextTick(() => {
 | 
			
		||||
				if (props.page.script && hpml.aiscript) {
 | 
			
		||||
					let ast;
 | 
			
		||||
					try {
 | 
			
		||||
						ast = parse(props.page.script);
 | 
			
		||||
					} catch (err) {
 | 
			
		||||
						console.error(err);
 | 
			
		||||
						/*os.alert({
 | 
			
		||||
							type: 'error',
 | 
			
		||||
							text: 'Syntax error :('
 | 
			
		||||
						});*/
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
					hpml.aiscript.exec(ast).then(() => {
 | 
			
		||||
						hpml.eval();
 | 
			
		||||
					}).catch(err => {
 | 
			
		||||
						console.error(err);
 | 
			
		||||
						/*os.alert({
 | 
			
		||||
							type: 'error',
 | 
			
		||||
							text: err
 | 
			
		||||
						});*/
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					hpml.eval();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			onUnmounted(() => {
 | 
			
		||||
				if (hpml.aiscript) hpml.aiscript.abort();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			hpml,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.iroscrza {
 | 
			
		||||
	&.serif {
 | 
			
		||||
		> div {
 | 
			
		||||
			font-family: serif;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	&.center {
 | 
			
		||||
		text-align: center;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
		Reference in New Issue
	
	Block a user