ダイアログ内でコードを表示するように

This commit is contained in:
kakkokari-gtyih 2024-06-29 21:10:03 +09:00
parent faead03310
commit 48d3b9a7b8
4 changed files with 181 additions and 70 deletions

16
locales/index.d.ts vendored
View File

@ -10103,6 +10103,10 @@ export interface Locale extends ILocale {
* 0
*/
"maxHeightWarn": string;
/**
*
*/
"previewIsNotActual": string;
/**
*
*/
@ -10116,9 +10120,17 @@ export interface Locale extends ILocale {
*/
"applyToPreview": string;
/**
*
*
*/
"previewIsNotActual": string;
"generateCode": string;
/**
*
*/
"codeGenerated": string;
/**
*
*/
"codeGeneratedDescription": string;
};
}
declare const locales: {

View File

@ -2695,7 +2695,10 @@ _embedCodeGen:
maxHeight: "高さの最大値"
maxHeightDescription: "0で最大値の設定が無効になります。ウィジェットが縦に伸び続けるのを防ぐために、何らかの値に指定してください。"
maxHeightWarn: "高さの最大値制限が無効0になっています。これが意図した変更ではない場合は、高さの最大値を何らかの値に設定してください。"
previewIsNotActual: "プレビュー画面で表示可能な範囲を超えたため、実際に埋め込んだ際とは表示が異なります。"
rounded: "角丸にする"
border: "外枠に枠線をつける"
applyToPreview: "プレビューに反映"
previewIsNotActual: "プレビュー画面で表示可能な範囲を超えたため、実際に埋め込んだ際とは表示が異なります。"
generateCode: "埋め込みコードを作成"
codeGenerated: "コードが生成されました"
codeGeneratedDescription: "生成されたコードをウェブサイトに貼り付けてご利用ください。"

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.codeBlockRoot">
<button :class="$style.codeBlockCopyButton" class="_button" @click="copy">
<button v-if="copyButton" :class="$style.codeBlockCopyButton" class="_button" @click="copy">
<i class="ti ti-copy"></i>
</button>
<Suspense>
@ -32,12 +32,17 @@ import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
const props = defineProps<{
const props = withDefaults(defineProps<{
code: string;
forceShow?: boolean;
copyButton?: boolean;
lang?: string;
}>();
}>(), {
copyButton: true,
forceShow: false,
});
const show = ref(!defaultStore.state.dataSaver.code);
const show = ref(props.forceShow === true ? true : !defaultStore.state.dataSaver.code);
const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'));

View File

@ -9,15 +9,21 @@ SPDX-License-Identifier: AGPL-3.0-only
:width="1000"
:height="600"
:scroll="false"
:withOkButton="true"
:withOkButton="false"
@close="cancel()"
@ok="ok()"
@closed="$emit('closed')"
>
<template #header>{{ i18n.ts._embedCodeGen.title }}</template>
<div :class="$style.embedCodeGenRoot">
<div :class="$style.embedCodeGenWrapper">
<Transition
mode="out-in"
:enterActiveClass="$style.transition_x_enterActive"
:leaveActiveClass="$style.transition_x_leaveActive"
:enterFromClass="$style.transition_x_enterFrom"
:leaveToClass="$style.transition_x_leaveTo"
>
<div v-if="phase === 'input'" key="input" :class="$style.embedCodeGenInputRoot">
<div
:class="$style.embedCodeGenPreviewRoot"
>
@ -60,9 +66,25 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo v-if="typeof maxHeight === 'number' && (maxHeight <= 0 || maxHeight > 700)">{{ i18n.ts._embedCodeGen.previewIsNotActual }}</MkInfo>
<div class="_buttons">
<MkButton :disabled="iframeLoading" @click="applyToPreview">{{ i18n.ts._embedCodeGen.applyToPreview }}</MkButton>
<MkButton :disabled="iframeLoading" primary @click="generate">{{ i18n.ts._embedCodeGen.generateCode }} <i class="ti ti-arrow-right"></i></MkButton>
</div>
</div>
</div>
<div v-else-if="phase === 'result'" key="result" :class="$style.embedCodeGenResultRoot">
<div :class="$style.embedCodeGenResultWrapper" class="_gaps">
<div class="_gaps_s">
<div :class="$style.embedCodeGenResultHeadingIcon"><i class="ti ti-check"></i></div>
<div :class="$style.embedCodeGenResultHeading">{{ i18n.ts._embedCodeGen.codeGenerated }}</div>
<div :class="$style.embedCodeGenResultDescription">{{ i18n.ts._embedCodeGen.codeGeneratedDescription }}</div>
</div>
<div class="_gaps_s">
<MkCode :code="result" lang="html" :forceShow="true" :copyButton="false" :class="$style.embedCodeGenResultCode"/>
<MkButton :class="$style.embedCodeGenResultButtons" rounded primary @click="doCopy"><i class="ti ti-copy"></i> {{ i18n.ts.copy }}</MkButton>
</div>
<MkButton :class="$style.embedCodeGenResultButtons" rounded transparent @click="close">{{ i18n.ts.close }}</MkButton>
</div>
</div>
</Transition>
</div>
</MkModalWindow>
</template>
@ -75,6 +97,8 @@ import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkButton from '@/components/MkButton.vue';
import MkCode from '@/components/MkCode.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os.js';
@ -86,19 +110,16 @@ import { embedRouteWithScrollbar } from '@/scripts/embed-page.js';
import type { EmbeddableEntity, EmbedParams } from '@/scripts/embed-page.js';
const emit = defineEmits<{
(ev: 'ok', url: string, code: string): void;
(ev: 'ok'): void;
(ev: 'cancel'): void;
(ev: 'closed'): void;
}>();
const props = withDefaults(defineProps<{
const props = defineProps<{
entity: EmbeddableEntity;
idOrUsername: string;
params?: EmbedParams;
doCopy?: boolean;
}>(), {
doCopy: true,
});
}>();
//#region Modal
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
@ -108,17 +129,11 @@ function cancel() {
dialogEl.value?.close();
}
function ok() {
const _idOrUsername = props.entity === 'user-timeline' ? '@' + props.idOrUsername : props.idOrUsername;
const generatedUrl = `${url}/embed/${props.entity}/${_idOrUsername}?${new URLSearchParams(normalizeEmbedParams(paramsForUrl.value)).toString()}`;
const generatedCode = getEmbedCode(`/embed/${props.entity}/${_idOrUsername}`, paramsForUrl.value);
if (props.doCopy) {
copy(generatedCode);
os.success();
}
emit('ok', generatedUrl, generatedCode);
function close() {
dialogEl.value?.close();
}
const phase = ref<'input' | 'result'>('input');
//#endregion
//#region URL
@ -143,7 +158,7 @@ const embedPreviewUrl = computed(() => {
const maxHeight = parseInt(paramClass.get('maxHeight')!);
paramClass.set('maxHeight', maxHeight === 0 ? '500' : Math.min(maxHeight, 700).toString()); // 700px
}
return `${url}/embed/${props.entity}/${_idOrUsername}${paramClass.toString() ? '?' + paramClass.toString() : ''}`;
return `http://localhost:3000/embed/${props.entity}/${_idOrUsername}${paramClass.toString() ? '?' + paramClass.toString() : ''}`;
});
const isEmbedWithScrollbar = computed(() => embedRouteWithScrollbar.includes(props.entity));
@ -174,6 +189,18 @@ function applyToPreview() {
}
});
}
const result = ref('');
function generate() {
const _idOrUsername = props.entity === 'user-timeline' ? '@' + props.idOrUsername : props.idOrUsername;
result.value = getEmbedCode(`/embed/${props.entity}/${_idOrUsername}`, paramsForUrl.value);
phase.value = 'result';
}
function doCopy() {
copy(result.value);
os.success();
}
//#endregion
//#region
@ -235,25 +262,48 @@ onMounted(() => {
resizeObserver.observe(resizerRootEl.value);
});
onDeactivated(() => {
function reset() {
window.removeEventListener('message', windowEventHandler);
resizeObserver.disconnect();
//
iframeHeight.value = 0;
iframeScale.value = 1;
iframeLoading.value = true;
result.value = '';
phase.value = 'input';
}
onDeactivated(() => {
reset();
});
onUnmounted(() => {
window.removeEventListener('message', windowEventHandler);
resizeObserver.disconnect();
reset();
});
//#endregion
</script>
<style module>
.transition_x_enterActive,
.transition_x_leaveActive {
transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
}
.transition_x_enterFrom {
opacity: 0;
transform: translateX(50px);
}
.transition_x_leaveTo {
opacity: 0;
transform: translateX(-50px);
}
.embedCodeGenRoot {
container-type: inline-size;
height: 100%;
}
.embedCodeGenWrapper {
.embedCodeGenInputRoot {
height: 100%;
display: grid;
grid-template-columns: 1fr 400px;
@ -319,6 +369,47 @@ onUnmounted(() => {
overflow-y: scroll;
}
.embedCodeGenResultRoot {
box-sizing: border-box;
padding: 24px;
height: 100%;
max-width: 700px;
margin: 0 auto;
display: flex;
align-items: center;
}
.embedCodeGenResultHeading {
text-align: center;
font-size: 1.2em;
}
.embedCodeGenResultHeadingIcon {
margin: 0 auto;
background-color: var(--accentedBg);
color: var(--accent);
text-align: center;
height: 64px;
width: 64px;
font-size: 24px;
line-height: 64px;
border-radius: 50%;
}
.embedCodeGenResultDescription {
text-align: center;
white-space: pre-wrap;
}
.embedCodeGenResultWrapper,
.embedCodeGenResultCode {
width: 100%;
}
.embedCodeGenResultButtons {
margin: 0 auto;
}
@container (max-width: 800px) {
.embedCodeGenWrapper {
grid-template-columns: 1fr;