Add form to create events in UI

This commit is contained in:
ssmucny 2023-04-20 22:01:36 -04:00
parent 07b3f19814
commit 8def7af701
2 changed files with 221 additions and 1 deletions

View File

@ -0,0 +1,183 @@
<template>
<div class="zmdxowut">
<MkInput v-model="title" small type="text" class="input">
<template #label>*{{ "Title" }}</template>
</MkInput>
<section>
<div>
<section>
<MkInput v-model="startDate" small type="date" class="input">
<template #label>*{{ "Start Date" }}</template>
</MkInput>
<MkInput v-model="startTime" small type="time" class="input">
<template #label>*{{ "Start Time" }}</template>
</MkInput>
</section>
<section>
<MkInput v-model="endDate" small type="date" class="input">
<template #label>{{ "End Date" }}</template>
</MkInput>
<MkInput v-model="endTime" small type="time" class="input">
<template #label>{{ "End Time" }}</template>
</MkInput>
</section>
</div>
</section>
<p>Details</p>
<ul>
<li v-for="(entry, i) in meta" :key="i">
<MkInput class="input" small :model-value="entry[0]" placeholder="placeholder key"
@update:model-value="onKeyInput(i, $event)">
</MkInput>
<MkInput class="input" small :model-value="entry[1]" placeholder="placeholder value"
@update:model-value="onValueInput(i, $event)">
</MkInput>
<button class="_button" @click="remove(i)">
<i class="ti ti-x"></i>
</button>
</li>
</ul>
<MkButton class="add" @click="add">{{ i18n.ts.add }}</MkButton>
</div>
</template>
<script lang="ts" setup>
import { Ref, ref, watch } from 'vue';
import MkInput from './MkInput.vue';
import MkButton from './MkButton.vue';
import { formatDateTimeString } from '@/scripts/format-time-string';
import { addTime } from '@/scripts/time';
import { i18n } from '@/i18n';
const props = defineProps<{
modelValue: {
title: string,
start: string,
end: string | null,
metadata: Record<string, string>,
}
}>();
const emit = defineEmits<{
(ev: 'update:modelValue', v: {
title: string,
start: string,
end: string | null,
metadata: Record<string, string>,
})
}>();
const title = ref(props.modelValue.title);
const startDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'));
const startTime = ref('00:00');
const endDate = ref('');
const endTime = ref('');
const meta = ref(Object.entries(props.modelValue.metadata));
function add() {
meta.value.push(['', '']);
}
function onKeyInput(i, newKey) {
meta.value[i][0] = newKey;
}
function onValueInput(i, value) {
meta.value[i][1] = value;
}
function remove(i) {
meta.value.splice(i, 1);
}
function get() {
const calcAt = (date: Ref<string>, time: Ref<string>): number => (new Date(`${date.value} ${time.value}`)).getTime();
return {
title: title.value,
start: calcAt(startDate, startTime),
end: endDate.value ? calcAt(endDate, endTime) : null,
metadata: meta.value.reduce((obj, [k, v]) => ({ ...obj, [k]: v }), {}),
};
}
watch([title, startDate, startTime, endDate, endTime, meta], () => emit('update:modelValue', get()), {
deep: true,
});
</script>
<style lang="scss" scoped>
.zmdxowut {
padding: 8px 16px;
>.caution {
margin: 0 0 8px 0;
font-size: 0.8em;
color: #f00;
>i {
margin-right: 4px;
}
}
>ul {
display: block;
margin: 0;
padding: 0;
list-style: none;
>li {
display: flex;
margin: 8px 0;
padding: 0;
width: 100%;
>.input {
flex: 1;
}
>button {
width: 32px;
padding: 4px 0;
}
}
}
>.add {
margin: 8px 0;
z-index: 1;
}
>section {
margin: 16px 0 0 0;
>div {
margin: 0 8px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
&:last-child {
flex: 1 0 auto;
>div {
flex-grow: 1;
}
>section {
// MAGIC: Prevent div above from growing unless wrapped to its own line
flex-grow: 9999;
align-items: end;
display: flex;
gap: 4px;
>.input {
flex: 1 1 auto;
}
}
}
}
}
}
</style>

View File

@ -49,6 +49,7 @@
<MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/>
<MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/>
<div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="ti ti-x"></i></button></div>
<MkEventEditor v-if="event" v-model="event" @destroyed="event = null"/>
<div v-if="visibility === 'specified'" :class="$style.toSpecified">
<span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span>
<div :class="$style.visibleUsers">
@ -75,6 +76,7 @@
<div :class="$style.footerLeft">
<button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button>
<button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button>
<button v-tooltip="i18n.ts.event" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: event }]" @click="toggleEvent"><i class="ti ti-calendar"></i></button>
<button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button>
<button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button>
<button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button>
@ -103,6 +105,7 @@ import MkNoteSimple from '@/components/MkNoteSimple.vue';
import MkNotePreview from '@/components/MkNotePreview.vue';
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
import MkPollEditor from '@/components/MkPollEditor.vue';
import MkEventEditor from '@/components/MkEventEditor.vue';
import { host, url } from '@/config';
import { erase, unique } from '@/scripts/array';
import { extractMentions } from '@/scripts/extract-mentions';
@ -165,6 +168,12 @@ let poll = $ref<{
expiresAt: string | null;
expiredAfter: string | null;
} | null>(null);
let event = $ref<{
title: string;
start: string;
end: string | null;
metadata: Record<string, string>;
} | null>(null);
let useCw = $ref(false);
let showPreview = $ref(false);
let cw = $ref<string | null>(null);
@ -235,7 +244,7 @@ const maxTextLength = $computed((): number => {
const canPost = $computed((): boolean => {
return !posting && !posted &&
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote || !!event) &&
(textLength <= maxTextLength) &&
(!poll || poll.choices.length >= 2);
});
@ -331,6 +340,7 @@ function watchForDraft() {
watch($$(useCw), () => saveDraft());
watch($$(cw), () => saveDraft());
watch($$(poll), () => saveDraft());
watch($$(event), () => saveDraft());
watch($$(files), () => saveDraft(), { deep: true });
watch($$(visibility), () => saveDraft());
watch($$(localOnly), () => saveDraft());
@ -375,6 +385,19 @@ function togglePoll() {
}
}
function toggleEvent() {
if (event) {
event = null;
} else {
event = {
title: '',
start: (new Date()).toString(),
end: null,
metadata: {},
};
}
}
function addTag(tag: string) {
insertTextAtCursor(textareaEl, ` #${tag} `);
}
@ -513,6 +536,7 @@ function clear() {
text = '';
files = [];
poll = null;
event = null;
quoteId = null;
}
@ -626,6 +650,7 @@ function saveDraft() {
localOnly: localOnly,
files: files,
poll: poll,
event: event,
},
};
@ -687,6 +712,7 @@ async function post(ev?: MouseEvent) {
renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined,
channelId: props.channel ? props.channel.id : undefined,
poll: poll,
event: event,
cw: useCw ? cw ?? '' : undefined,
localOnly: localOnly,
visibility: visibility,
@ -847,6 +873,9 @@ onMounted(() => {
if (draft.data.poll) {
poll = draft.data.poll;
}
if (draft.data.event) {
event = draft.data.event;
}
}
}
@ -865,6 +894,14 @@ onMounted(() => {
expiredAfter: init.poll.expiredAfter,
};
}
if (init.event) {
event = {
title: init.event.title,
start: init.event.start,
end: init.event.end,
metadata: init.event.metadata,
};
}
visibility = init.visibility;
localOnly = init.localOnly;
quoteId = init.renote ? init.renote.id : null;