summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkSelect.vue
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/components/MkSelect.vue')
-rw-r--r--packages/frontend/src/components/MkSelect.vue98
1 files changed, 47 insertions, 51 deletions
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index 150a5c6d54..79a56b68a8 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -16,9 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
@keydown.space.enter="show"
>
<div ref="prefixEl" :class="$style.prefix"><slot name="prefix"></slot></div>
- <select
+ <div
ref="inputEl"
- v-model="v"
v-adaptive-border
tabindex="-1"
:class="$style.inputCore"
@@ -26,55 +25,48 @@ SPDX-License-Identifier: AGPL-3.0-only
:required="required"
:readonly="readonly"
:placeholder="placeholder"
- @input="onInput"
@mousedown.prevent="() => {}"
@keydown.prevent="() => {}"
>
- <slot></slot>
- </select>
+ <div style="pointer-events: none;">{{ currentValueText ?? '' }}</div>
+ <div style="display: none;">
+ <slot></slot>
+ </div>
+ </div>
<div ref="suffixEl" :class="$style.suffix"><i class="ti ti-chevron-down" :class="[$style.chevron, { [$style.chevronOpening]: opening }]"></i></div>
</div>
<div :class="$style.caption"><slot name="caption"></slot></div>
-
- <MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
<script lang="ts" setup>
import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots, VNodeChild } from 'vue';
-import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
import { useInterval } from '@@/js/use-interval.js';
-import { i18n } from '@/i18n.js';
import type { MenuItem } from '@/types/menu.js';
+import * as os from '@/os.js';
const props = defineProps<{
- modelValue: string | null;
+ modelValue: string | number | null;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
placeholder?: string;
autofocus?: boolean;
inline?: boolean;
- manualSave?: boolean;
small?: boolean;
large?: boolean;
}>();
const emit = defineEmits<{
- (ev: 'changeByUser', value: string | null): void;
- (ev: 'update:modelValue', value: string | null): void;
+ (ev: 'update:modelValue', value: string | number | null): void;
}>();
const slots = useSlots();
const { modelValue, autofocus } = toRefs(props);
-const v = ref(modelValue.value);
const focused = ref(false);
const opening = ref(false);
-const changed = ref(false);
-const invalid = ref(false);
-const filled = computed(() => v.value !== '' && v.value != null);
+const currentValueText = ref<string | null>(null);
const inputEl = ref<HTMLObjectElement | null>(null);
const prefixEl = ref<HTMLElement | null>(null);
const suffixEl = ref<HTMLElement | null>(null);
@@ -85,26 +77,6 @@ const height =
36;
const focus = () => container.value?.focus();
-const onInput = (ev) => {
- changed.value = true;
-};
-
-const updated = () => {
- changed.value = false;
- emit('update:modelValue', v.value);
-};
-
-watch(modelValue, newValue => {
- v.value = newValue;
-});
-
-watch(v, () => {
- if (!props.manualSave) {
- updated();
- }
-
- invalid.value = inputEl.value?.validity.badInput ?? true;
-});
// このコンポーネントが作成された時、非表示状態である場合がある
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
@@ -134,6 +106,31 @@ onMounted(() => {
});
});
+watch(modelValue, () => {
+ const scanOptions = (options: VNodeChild[]) => {
+ for (const vnode of options) {
+ if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
+ if (vnode.type === 'optgroup') {
+ const optgroup = vnode;
+ if (Array.isArray(optgroup.children)) scanOptions(optgroup.children);
+ } else if (Array.isArray(vnode.children)) { // 何故かフラグメントになってくることがある
+ const fragment = vnode;
+ if (Array.isArray(fragment.children)) scanOptions(fragment.children);
+ } else if (vnode.props == null) { // v-if で条件が false のときにこうなる
+ // nop?
+ } else {
+ const option = vnode;
+ if (option.props?.value === modelValue.value) {
+ currentValueText.value = option.children as string;
+ break;
+ }
+ }
+ }
+ };
+
+ scanOptions(slots.default!());
+}, { immediate: true });
+
function show() {
if (opening.value) return;
focus();
@@ -146,11 +143,9 @@ function show() {
const pushOption = (option: VNode) => {
menu.push({
text: option.children as string,
- active: computed(() => v.value === option.props?.value),
+ active: computed(() => modelValue.value === option.props?.value),
action: () => {
- v.value = option.props?.value;
- changed.value = true;
- emit('changeByUser', v.value);
+ emit('update:modelValue', option.props?.value);
},
});
};
@@ -202,7 +197,7 @@ function show() {
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -220,8 +215,8 @@ function show() {
&.focused {
> .inputCore {
- border-color: var(--accent) !important;
- //box-shadow: 0 0 0 4px var(--focus);
+ border-color: var(--MI_THEME-accent) !important;
+ //box-shadow: 0 0 0 4px var(--MI_THEME-focus);
}
}
@@ -240,7 +235,7 @@ function show() {
&:hover {
> .inputCore {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
}
@@ -248,7 +243,8 @@ function show() {
.inputCore {
appearance: none;
-webkit-appearance: none;
- display: block;
+ display: flex;
+ align-items: center;
height: v-bind("height + 'px'");
width: 100%;
margin: 0;
@@ -256,10 +252,10 @@ function show() {
font: inherit;
font-weight: normal;
font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--panel);
- border-radius: var(--radius-sm);
+ color: var(--MI_THEME-fg);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-panel);
+ border-radius: var(--MI-radius-sm);
outline: none;
box-shadow: none;
box-sizing: border-box;