summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-01-26 03:26:40 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-01-26 03:26:40 +0900
commitf7a90e041c24d542579dc3cf5a6f674a7a779446 (patch)
treef8083153bf371f7e16f6e951a7273f6f2d69261b
parentrefactor (diff)
parentenhance: Improve poll-editor UI + composition port (#8186) (diff)
downloadmisskey-f7a90e041c24d542579dc3cf5a6f674a7a779446.tar.gz
misskey-f7a90e041c24d542579dc3cf5a6f674a7a779446.tar.bz2
misskey-f7a90e041c24d542579dc3cf5a6f674a7a779446.zip
Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
-rw-r--r--packages/client/src/components/poll-editor.vue212
-rw-r--r--packages/client/src/components/post-form.vue17
2 files changed, 95 insertions, 134 deletions
diff --git a/packages/client/src/components/poll-editor.vue b/packages/client/src/components/poll-editor.vue
index fad0cf1593..d8f898ee13 100644
--- a/packages/client/src/components/poll-editor.vue
+++ b/packages/client/src/components/poll-editor.vue
@@ -3,7 +3,7 @@
<p v-if="choices.length < 2" class="caution">
<i class="fas fa-exclamation-triangle"></i>{{ $ts._poll.noOnlyOneChoice }}
</p>
- <ul ref="choices">
+ <ul>
<li v-for="(choice, i) in choices" :key="i">
<MkInput class="input" :model-value="choice" :placeholder="$t('_poll.choiceN', { n: i + 1 })" @update:modelValue="onInput(i, $event)">
</MkInput>
@@ -14,8 +14,8 @@
</ul>
<MkButton v-if="choices.length < 10" class="add" @click="add">{{ $ts.add }}</MkButton>
<MkButton v-else class="add" disabled>{{ $ts._poll.noMore }}</MkButton>
+ <MkSwitch v-model="multiple">{{ $ts._poll.canMultipleVote }}</MkSwitch>
<section>
- <MkSwitch v-model="multiple">{{ $ts._poll.canMultipleVote }}</MkSwitch>
<div>
<MkSelect v-model="expiration">
<template #label>{{ $ts._poll.expiration }}</template>
@@ -31,7 +31,7 @@
<template #label>{{ $ts._poll.deadlineTime }}</template>
</MkInput>
</section>
- <section v-if="expiration === 'after'">
+ <section v-else-if="expiration === 'after'">
<MkInput v-model="after" type="number" class="input">
<template #label>{{ $ts._poll.duration }}</template>
</MkInput>
@@ -47,8 +47,8 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
import { addTime } from '@/scripts/time';
import { formatDateTimeString } from '@/scripts/format-time-string';
import MkInput from './form/input.vue';
@@ -56,125 +56,85 @@ import MkSelect from './form/select.vue';
import MkSwitch from './form/switch.vue';
import MkButton from './ui/button.vue';
-export default defineComponent({
- components: {
- MkInput,
- MkSelect,
- MkSwitch,
- MkButton,
- },
+const props = defineProps<{
+ modelValue: {
+ expiresAt: string;
+ expiredAfter: number;
+ choices: string[];
+ multiple: boolean;
+ };
+}>();
+const emit = defineEmits<{
+ (ev: 'update:modelValue', v: {
+ expiresAt: string;
+ expiredAfter: number;
+ choices: string[];
+ multiple: boolean;
+ }): void;
+}>();
- props: {
- poll: {
- type: Object,
- required: true
- }
- },
-
- emits: ['updated'],
-
- data() {
- return {
- choices: this.poll.choices,
- multiple: this.poll.multiple,
- expiration: 'infinite',
- atDate: formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'),
- atTime: '00:00',
- after: 0,
- unit: 'second',
- };
- },
+const choices = ref(props.modelValue.choices);
+const multiple = ref(props.modelValue.multiple);
+const expiration = ref('infinite');
+const atDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'));
+const atTime = ref('00:00');
+const after = ref(0);
+const unit = ref('second');
- watch: {
- choices: {
- handler() {
- this.$emit('updated', this.get());
- },
- deep: true
- },
- multiple: {
- handler() {
- this.$emit('updated', this.get());
- },
- },
- expiration: {
- handler() {
- this.$emit('updated', this.get());
- },
- },
- atDate: {
- handler() {
- this.$emit('updated', this.get());
- },
- },
- after: {
- handler() {
- this.$emit('updated', this.get());
- },
- },
- unit: {
- handler() {
- this.$emit('updated', this.get());
- },
- },
- },
+if (props.modelValue.expiresAt) {
+ expiration.value = 'at';
+ atDate.value = atTime.value = props.modelValue.expiresAt;
+} else if (typeof props.modelValue.expiredAfter === 'number') {
+ expiration.value = 'after';
+ after.value = props.modelValue.expiredAfter / 1000;
+} else {
+ expiration.value = 'infinite';
+}
- created() {
- const poll = this.poll;
- if (poll.expiresAt) {
- this.expiration = 'at';
- this.atDate = this.atTime = poll.expiresAt;
- } else if (typeof poll.expiredAfter === 'number') {
- this.expiration = 'after';
- this.after = poll.expiredAfter / 1000;
- } else {
- this.expiration = 'infinite';
- }
- },
+function onInput(i, value) {
+ choices.value[i] = value;
+}
- methods: {
- onInput(i, e) {
- this.choices[i] = e;
- },
+function add() {
+ choices.value.push('');
+ // TODO
+ // nextTick(() => {
+ // (this.$refs.choices as any).childNodes[this.choices.length - 1].childNodes[0].focus();
+ // });
+}
- add() {
- this.choices.push('');
- this.$nextTick(() => {
- // TODO
- //(this.$refs.choices as any).childNodes[this.choices.length - 1].childNodes[0].focus();
- });
- },
+function remove(i) {
+ choices.value = choices.value.filter((_, _i) => _i != i);
+}
- remove(i) {
- this.choices = this.choices.filter((_, _i) => _i != i);
- },
+function get() {
+ const calcAt = () => {
+ return new Date(`${atDate.value} ${atTime.value}`).getTime();
+ };
- get() {
- const at = () => {
- return new Date(`${this.atDate} ${this.atTime}`).getTime();
- };
+ const calcAfter = () => {
+ let base = parseInt(after.value);
+ switch (unit.value) {
+ case 'day': base *= 24;
+ case 'hour': base *= 60;
+ case 'minute': base *= 60;
+ case 'second': return base *= 1000;
+ default: return null;
+ }
+ };
- const after = () => {
- let base = parseInt(this.after);
- switch (this.unit) {
- case 'day': base *= 24;
- case 'hour': base *= 60;
- case 'minute': base *= 60;
- case 'second': return base *= 1000;
- default: return null;
- }
- };
+ return {
+ choices: choices.value,
+ multiple: multiple.value,
+ ...(
+ expiration.value === 'at' ? { expiresAt: calcAt() } :
+ expiration.value === 'after' ? { expiredAfter: calcAfter() } : {}
+ )
+ };
+}
- return {
- choices: this.choices,
- multiple: this.multiple,
- ...(
- this.expiration === 'at' ? { expiresAt: at() } :
- this.expiration === 'after' ? { expiredAfter: after() } : {}
- )
- };
- },
- }
+watch([choices, multiple, expiration, atDate, atTime, after, unit], () => emit('update:modelValue', get()), {
+ deep: true,
});
</script>
@@ -216,7 +176,7 @@ export default defineComponent({
}
> .add {
- margin: 8px 0 0 0;
+ margin: 8px 0;
z-index: 1;
}
@@ -225,21 +185,27 @@ export default defineComponent({
> 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 {
- align-items: center;
+ // MAGIC: Prevent div above from growing unless wrapped to its own line
+ flex-grow: 9999;
+ align-items: end;
display: flex;
- margin: -32px 0 0;
-
- > &:first-child {
- margin-right: 16px;
- }
+ gap: 4px;
> .input {
- flex: 1 0 auto;
+ flex: 1 1 auto;
}
}
}
diff --git a/packages/client/src/components/post-form.vue b/packages/client/src/components/post-form.vue
index 0dcec26932..ed78c5a3fb 100644
--- a/packages/client/src/components/post-form.vue
+++ b/packages/client/src/components/post-form.vue
@@ -43,7 +43,7 @@
<textarea ref="textareaEl" v-model="text" class="text" :class="{ withCw: useCw }" :disabled="posting" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" class="hashtags" :placeholder="i18n.locale.hashtags" list="hashtags">
<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/>
- <XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
+ <XPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/>
<XNotePreview v-if="showPreview" class="preview" :text="text"/>
<footer>
<button v-tooltip="i18n.locale.attachFile" class="_button" @click="chooseFileFrom"><i class="fas fa-photo-video"></i></button>
@@ -111,9 +111,9 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'posted'): void;
- (e: 'cancel'): void;
- (e: 'esc'): void;
+ (ev: 'posted'): void;
+ (ev: 'cancel'): void;
+ (ev: 'esc'): void;
}>();
const textareaEl = $ref<HTMLTextAreaElement | null>(null);
@@ -127,8 +127,8 @@ let files = $ref(props.initialFiles ?? []);
let poll = $ref<{
choices: string[];
multiple: boolean;
- expiresAt: string;
- expiredAfter: string;
+ expiresAt: string | null;
+ expiredAfter: string | null;
} | null>(null);
let useCw = $ref(false);
let showPreview = $ref(false);
@@ -371,11 +371,6 @@ function upload(file: File, name?: string) {
});
}
-function onPollUpdate(poll) {
- poll = poll;
- saveDraft();
-}
-
function setVisibility() {
if (props.channel) {
// TODO: information dialog