mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-05-08 22:56:56 +00:00
to keep things manageable i merged a lot of one off values into just a handful of common sizes, so some parts of the ui will look different than upstream even with the "Misskey" rounding mode
197 lines
3.9 KiB
Vue
197 lines
3.9 KiB
Vue
<!--
|
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
-->
|
|
|
|
<template>
|
|
<div>
|
|
<div :class="$style.label" @click="focus"><slot name="label"></slot></div>
|
|
<div :class="{ [$style.disabled]: disabled, [$style.focused]: focused, [$style.tall]: tall, [$style.pre]: pre }" style="position: relative;">
|
|
<textarea
|
|
ref="inputEl"
|
|
v-model="v"
|
|
v-adaptive-border
|
|
:class="[$style.textarea, { _monospace: code }]"
|
|
:disabled="disabled"
|
|
:required="required"
|
|
:readonly="readonly"
|
|
:placeholder="placeholder"
|
|
:pattern="pattern"
|
|
:autocomplete="autocomplete"
|
|
:spellcheck="spellcheck"
|
|
@focus="focused = true"
|
|
@blur="focused = false"
|
|
@keydown="onKeydown($event)"
|
|
@input="onInput"
|
|
></textarea>
|
|
</div>
|
|
<div :class="$style.caption"><slot name="caption"></slot></div>
|
|
|
|
<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ph-floppy-disk ph-bold pg-lg"></i> {{ i18n.ts.save }}</MkButton>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { onMounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue';
|
|
import { debounce } from 'throttle-debounce';
|
|
import MkButton from '@/components/MkButton.vue';
|
|
import { i18n } from '@/i18n.js';
|
|
|
|
const props = defineProps<{
|
|
modelValue: string | null;
|
|
required?: boolean;
|
|
readonly?: boolean;
|
|
disabled?: boolean;
|
|
pattern?: string;
|
|
placeholder?: string;
|
|
autofocus?: boolean;
|
|
autocomplete?: string;
|
|
spellcheck?: boolean;
|
|
debounce?: boolean;
|
|
manualSave?: boolean;
|
|
code?: boolean;
|
|
tall?: boolean;
|
|
pre?: boolean;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(ev: 'change', _ev: KeyboardEvent): void;
|
|
(ev: 'keydown', _ev: KeyboardEvent): void;
|
|
(ev: 'enter'): void;
|
|
(ev: 'update:modelValue', value: string): void;
|
|
}>();
|
|
|
|
const { modelValue, autofocus } = toRefs(props);
|
|
const v = ref<string>(modelValue.value ?? '');
|
|
const focused = ref(false);
|
|
const changed = ref(false);
|
|
const invalid = ref(false);
|
|
const filled = computed(() => v.value !== '' && v.value != null);
|
|
const inputEl = shallowRef<HTMLTextAreaElement>();
|
|
|
|
const focus = () => inputEl.value.focus();
|
|
const onInput = (ev) => {
|
|
changed.value = true;
|
|
emit('change', ev);
|
|
};
|
|
const onKeydown = (ev: KeyboardEvent) => {
|
|
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
|
|
|
|
emit('keydown', ev);
|
|
|
|
if (ev.code === 'Enter') {
|
|
emit('enter');
|
|
}
|
|
};
|
|
|
|
const updated = () => {
|
|
changed.value = false;
|
|
emit('update:modelValue', v.value ?? '');
|
|
};
|
|
|
|
const debouncedUpdated = debounce(1000, updated);
|
|
|
|
watch(modelValue, newValue => {
|
|
v.value = newValue;
|
|
});
|
|
|
|
watch(v, newValue => {
|
|
if (!props.manualSave) {
|
|
if (props.debounce) {
|
|
debouncedUpdated();
|
|
} else {
|
|
updated();
|
|
}
|
|
}
|
|
|
|
invalid.value = inputEl.value.validity.badInput;
|
|
});
|
|
|
|
onMounted(() => {
|
|
nextTick(() => {
|
|
if (autofocus.value) {
|
|
focus();
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" module>
|
|
.label {
|
|
font-size: 0.85em;
|
|
padding: 0 0 8px 0;
|
|
user-select: none;
|
|
|
|
&:empty {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.caption {
|
|
font-size: 0.85em;
|
|
padding: 8px 0 0 0;
|
|
color: var(--fgTransparentWeak);
|
|
|
|
&:empty {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.textarea {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
display: block;
|
|
width: 100%;
|
|
min-width: 100%;
|
|
max-width: 100%;
|
|
min-height: 130px;
|
|
margin: 0;
|
|
padding: 12px;
|
|
font: inherit;
|
|
font-weight: normal;
|
|
font-size: 1em;
|
|
color: var(--fg);
|
|
background: var(--panel);
|
|
border: solid 1px var(--panel);
|
|
border-radius: var(--radius-sm);
|
|
outline: none;
|
|
box-shadow: none;
|
|
box-sizing: border-box;
|
|
transition: border-color 0.1s ease-out;
|
|
|
|
&:hover {
|
|
border-color: var(--inputBorderHover) !important;
|
|
}
|
|
}
|
|
|
|
.focused {
|
|
> .textarea {
|
|
border-color: var(--accent) !important;
|
|
}
|
|
}
|
|
|
|
.disabled {
|
|
opacity: 0.7;
|
|
cursor: not-allowed !important;
|
|
|
|
> .textarea {
|
|
cursor: not-allowed !important;
|
|
}
|
|
}
|
|
|
|
.tall {
|
|
> .textarea {
|
|
min-height: 200px;
|
|
}
|
|
}
|
|
|
|
.pre {
|
|
> .textarea {
|
|
white-space: pre;
|
|
}
|
|
}
|
|
|
|
.save {
|
|
margin: 8px 0 0 0;
|
|
}
|
|
</style>
|