mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-18 22:33:50 +02:00
replace el-input with InputGroup
This commit is contained in:
@@ -7,9 +7,8 @@
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="bottom" align="start" class="w-155">
|
||||
<div class="icon-picker">
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="search"
|
||||
clearable
|
||||
class="icon-picker__search"
|
||||
:placeholder="t('nav_menu.icon_picker.search_placeholder')" />
|
||||
<el-scrollbar v-if="filteredCategories.length" height="600px" class="icon-picker__scroll">
|
||||
@@ -47,6 +46,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
|
||||
|
||||
@@ -402,16 +402,13 @@
|
||||
<div class="x-friend-item" style="width: 100%; cursor: default">
|
||||
<div class="detail">
|
||||
<span class="name" style="margin-bottom: 5px">{{ t('dialog.avatar.info.memo') }}</span>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="memo"
|
||||
class="extra"
|
||||
size="small"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:autosize="{ minRows: 1, maxRows: 20 }"
|
||||
:placeholder="t('dialog.avatar.info.memo_placeholder')"
|
||||
resize="none"
|
||||
@change="onAvatarMemoChange"></el-input>
|
||||
input-class="resize-none min-h-0"
|
||||
@change="onAvatarMemoChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="width: 100%; cursor: default">
|
||||
@@ -545,6 +542,7 @@
|
||||
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -56,14 +56,13 @@
|
||||
|
||||
<div style="font-size: 12px">{{ t('dialog.set_world_tags.author_tags') }}</div>
|
||||
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
:model-value="setAvatarStylesDialog.authorTags"
|
||||
type="textarea"
|
||||
size="small"
|
||||
show-word-limit
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
:rows="2"
|
||||
placeholder=""
|
||||
style="margin-top: 10px"
|
||||
input-class="resize-none"
|
||||
@update:modelValue="(v) => updateDialog({ authorTags: v })" />
|
||||
</template>
|
||||
|
||||
@@ -80,6 +79,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { watch } from 'vue';
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
<span>{{ t('dialog.set_avatar_tags.content_sex') }}</span>
|
||||
</label>
|
||||
<br />
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="setAvatarTagsDialog.selectedTagsCsv"
|
||||
size="small"
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
:rows="2"
|
||||
:placeholder="t('dialog.set_avatar_tags.custom_tags_placeholder')"
|
||||
style="margin-top: 10px"
|
||||
@input="updateInputAvatarTags"></el-input>
|
||||
input-class="resize-none"
|
||||
@input="updateInputAvatarTags" />
|
||||
<br />
|
||||
<br />
|
||||
<template
|
||||
@@ -101,6 +101,7 @@
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
destroy-on-close>
|
||||
<div class="folder-editor">
|
||||
<div class="folder-editor__form">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="folderEditor.data.name"
|
||||
:placeholder="t('nav_menu.custom_nav.folder_name_placeholder')" />
|
||||
<IconPicker v-model="folderEditor.data.icon" class="folder-editor__icon-picker" />
|
||||
@@ -219,6 +219,7 @@
|
||||
|
||||
import { Badge } from '../ui/badge';
|
||||
import { Checkbox } from '../ui/checkbox';
|
||||
import { InputGroupField } from '../ui/input-group';
|
||||
import { navDefinitions } from '../../shared/constants/ui.js';
|
||||
|
||||
import IconPicker from '../IconPicker.vue';
|
||||
|
||||
@@ -723,10 +723,10 @@
|
||||
<span style="margin-right: 10px; vertical-align: top"
|
||||
>{{ t('dialog.group.posts.posts_count') }} {{ groupDialog.posts.length }}</span
|
||||
>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="groupDialog.postsSearch"
|
||||
clearable
|
||||
size="small"
|
||||
size="sm"
|
||||
:placeholder="t('dialog.group.posts.search_placeholder')"
|
||||
style="width: 89%; margin-bottom: 10px"
|
||||
@input="updateGroupPostSearch" />
|
||||
@@ -908,11 +908,11 @@
|
||||
</VirtualCombobox>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="groupDialog.memberSearch"
|
||||
:disabled="!hasGroupPermission(groupDialog.ref, 'group-members-manage')"
|
||||
clearable
|
||||
size="small"
|
||||
size="sm"
|
||||
:placeholder="t('dialog.group.members.search')"
|
||||
style="margin-top: 10px; margin-bottom: 10px"
|
||||
@input="groupMembersSearch" />
|
||||
@@ -1162,6 +1162,7 @@
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { RefreshCcw } from 'lucide-vue-next';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { VirtualCombobox } from '@/components/ui/virtual-combobox';
|
||||
|
||||
@@ -105,14 +105,14 @@
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="memberSearch"
|
||||
:disabled="!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')"
|
||||
clearable
|
||||
size="small"
|
||||
size="sm"
|
||||
:placeholder="t('dialog.group.members.search')"
|
||||
style="margin-top: 10px; margin-bottom: 10px"
|
||||
@input="groupMembersSearch"></el-input>
|
||||
@input="groupMembersSearch" />
|
||||
<br />
|
||||
<Button size="sm" variant="outline" @click="selectAllGroupMembers">{{
|
||||
t('dialog.group_member_moderation.select_all')
|
||||
@@ -225,12 +225,12 @@
|
||||
groupBansModerationTable.data.length
|
||||
}}</span>
|
||||
<br />
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="groupBansModerationTable.filters[0].value"
|
||||
clearable
|
||||
size="small"
|
||||
size="sm"
|
||||
:placeholder="t('dialog.group.members.search')"
|
||||
style="margin-top: 10px; margin-bottom: 10px"></el-input>
|
||||
style="margin-top: 10px; margin-bottom: 10px" />
|
||||
<br />
|
||||
<Button size="sm" variant="outline" @click="selectAllGroupBans">{{
|
||||
t('dialog.group_member_moderation.select_all')
|
||||
@@ -644,12 +644,12 @@
|
||||
}}</Button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="groupLogsModerationTable.filters[0].value"
|
||||
clearable
|
||||
size="small"
|
||||
size="sm"
|
||||
:placeholder="t('dialog.group.members.search')"
|
||||
style="margin-top: 10px; margin-bottom: 10px"></el-input>
|
||||
style="margin-top: 10px; margin-bottom: 10px" />
|
||||
<br />
|
||||
<DataTable v-bind="groupLogsModerationTable" style="margin-top: 10px">
|
||||
<el-table-column
|
||||
@@ -708,12 +708,12 @@
|
||||
<br />
|
||||
<span class="name">{{ t('dialog.group_member_moderation.user_id') }}</span>
|
||||
<br />
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="selectUserId"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="margin-top: 5px; width: 340px"
|
||||
:placeholder="t('dialog.group_member_moderation.user_id_placeholder')"
|
||||
clearable></el-input>
|
||||
clearable />
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@@ -765,16 +765,13 @@
|
||||
<br />
|
||||
<br />
|
||||
<span class="name">{{ t('dialog.group_member_moderation.notes') }}</span>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="note"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:autosize="{ minRows: 1, maxRows: 20 }"
|
||||
:placeholder="t('dialog.group_member_moderation.note_placeholder')"
|
||||
size="small"
|
||||
resize="none"
|
||||
style="margin-top: 5px"></el-input>
|
||||
style="margin-top: 5px"
|
||||
input-class="resize-none min-h-0" />
|
||||
<br />
|
||||
<br />
|
||||
<span class="name">{{ t('dialog.group_member_moderation.selected_roles') }}</span>
|
||||
@@ -870,6 +867,7 @@
|
||||
<script setup>
|
||||
import { ArrowDown, Loading, Refresh, Warning } from '@element-plus/icons-vue';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { Trash2 } from 'lucide-vue-next';
|
||||
|
||||
@@ -18,14 +18,12 @@
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="groupLogsExportContent"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
input-class="resize-none"
|
||||
@click="handleCopyGroupLogsExportContent" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
@@ -33,6 +31,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { copyToClipboard } from '../../../shared/utils';
|
||||
|
||||
@@ -8,16 +8,14 @@
|
||||
<h3 v-text="groupPostEditDialog.groupRef.name"></h3>
|
||||
<el-form :model="groupPostEditDialog" label-width="150px">
|
||||
<el-form-item :label="t('dialog.group_post_edit.title')">
|
||||
<el-input v-model="groupPostEditDialog.title" size="small"></el-input>
|
||||
<InputGroupField v-model="groupPostEditDialog.title" size="sm" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.group_post_edit.message')">
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="groupPostEditDialog.text"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:autosize="{ minRows: 4, maxRows: 20 }"
|
||||
style="margin-top: 10px"
|
||||
resize="none"></el-input>
|
||||
input-class="resize-none" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<label v-if="!groupPostEditDialog.postId" class="inline-flex items-center gap-2">
|
||||
@@ -111,6 +109,7 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<span>{{ t('dialog.edit_send_invite_message.description') }}</span>
|
||||
</div>
|
||||
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="editAndSendInviteDialog.newMessage"
|
||||
:maxlength="64"
|
||||
multiline
|
||||
rows="2"
|
||||
:rows="2"
|
||||
class="mt-2.5"
|
||||
placeholder="" />
|
||||
placeholder=""
|
||||
show-count />
|
||||
|
||||
<template #footer>
|
||||
<Button variant="secondary" class="mr-2" @click="cancelEditAndSendInvite">
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<el-dialog :z-index="launchDialogIndex" v-model="isVisible" :title="t('dialog.launch.header')" width="450px">
|
||||
<el-form :model="launchDialog" label-width="100px">
|
||||
<el-form-item :label="t('dialog.launch.url')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="launchDialog.url"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="width: 230px"
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
||||
@@ -26,9 +26,9 @@
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="launchDialog.shortUrl"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="width: 230px"
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
||||
@@ -42,9 +42,9 @@
|
||||
</TooltipWrapper>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.launch.location')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="launchDialog.location"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="width: 230px"
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
||||
@@ -130,6 +130,7 @@
|
||||
import { ButtonGroup } from '@/components/ui/button-group';
|
||||
import { Copy } from 'lucide-vue-next';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { MoreHorizontal } from 'lucide-vue-next';
|
||||
import { Warning } from '@element-plus/icons-vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -115,12 +115,12 @@
|
||||
@update:modelValue="buildInstance" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.new_instance.display_name')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
:disabled="!isLocalUserVrcPlusSupporter"
|
||||
v-model="newInstanceDialog.displayName"
|
||||
size="small"
|
||||
size="sm"
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||
@change="buildInstance"></el-input>
|
||||
@change="buildInstance" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="newInstanceDialog.accessType === 'group'"
|
||||
@@ -177,14 +177,14 @@
|
||||
</el-form-item>
|
||||
<template v-if="newInstanceDialog.instanceCreated">
|
||||
<el-form-item :label="t('dialog.new_instance.location')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="newInstanceDialog.location"
|
||||
size="small"
|
||||
size="sm"
|
||||
readonly
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()"></el-input>
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.new_instance.url')">
|
||||
<el-input v-model="newInstanceDialog.url" size="small" readonly></el-input>
|
||||
<InputGroupField v-model="newInstanceDialog.url" size="sm" readonly />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
@@ -275,18 +275,18 @@
|
||||
<Checkbox v-model="newInstanceDialog.ageGate" @update:modelValue="buildInstance" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.new_instance.world_id')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="newInstanceDialog.worldId"
|
||||
size="small"
|
||||
size="sm"
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||
@change="buildLegacyInstance"></el-input>
|
||||
@change="buildLegacyInstance" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.new_instance.instance_id')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="newInstanceDialog.instanceName"
|
||||
:placeholder="t('dialog.new_instance.instance_id_placeholder')"
|
||||
size="small"
|
||||
@change="buildLegacyInstance"></el-input>
|
||||
size="sm"
|
||||
@change="buildLegacyInstance" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="
|
||||
@@ -352,14 +352,14 @@
|
||||
</VirtualCombobox>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.new_instance.location')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="newInstanceDialog.location"
|
||||
size="small"
|
||||
size="sm"
|
||||
readonly
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()"></el-input>
|
||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('dialog.new_instance.url')">
|
||||
<el-input v-model="newInstanceDialog.url" size="small" readonly></el-input>
|
||||
<InputGroupField v-model="newInstanceDialog.url" size="sm" readonly />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
@@ -443,6 +443,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Check as CheckIcon } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
append-to-body>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<span style="font-size: 14px" v-text="previousInstancesGroupDialog.groupRef.name"></span>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="previousInstancesGroupDialogTable.filters[0].value"
|
||||
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||
style="width: 150px" />
|
||||
@@ -70,6 +70,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
@close="closeDialog">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<Location :location="location.tag" style="font-size: 14px" />
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="dataTable.filters[0].value"
|
||||
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||
style="width: 150px"
|
||||
clearable></el-input>
|
||||
clearable />
|
||||
</div>
|
||||
<DataTable :loading="loading" v-bind="dataTable" style="margin-top: 10px">
|
||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="130">
|
||||
@@ -64,6 +64,7 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { compareByCreatedAt, formatDateFilter, parseLocation, timeToText } from '../../../shared/utils';
|
||||
import { InputGroupField } from '../../../components/ui/input-group';
|
||||
import { useGameLogStore, useInstanceStore, useUserStore } from '../../../stores';
|
||||
import { database } from '../../../service/database';
|
||||
import { getNextDialogIndex } from '../../../shared/utils/base/ui';
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
append-to-body>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<span style="font-size: 14px" v-text="previousInstancesWorldDialog.worldRef.name"></span>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="previousInstancesWorldDialogTable.filters[0].value"
|
||||
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||
style="display: block; width: 150px"></el-input>
|
||||
style="display: block; width: 150px" />
|
||||
</div>
|
||||
<DataTable :loading="loading" v-bind="previousInstancesWorldDialogTable" style="margin-top: 10px">
|
||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||
@@ -74,6 +74,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -6,25 +6,29 @@
|
||||
width="600px"
|
||||
append-to-body>
|
||||
<div v-loading="bioDialog.loading">
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="bioDialog.bio"
|
||||
:maxlength="512"
|
||||
multiline
|
||||
rows="5"
|
||||
:rows="5"
|
||||
:placeholder="t('dialog.bio.bio_placeholder')"
|
||||
class="mb-2.5" />
|
||||
class="mb-2.5"
|
||||
show-count />
|
||||
|
||||
<el-input
|
||||
<InputGroupAction
|
||||
v-for="(link, index) in bioDialog.bioLinks"
|
||||
:key="index"
|
||||
v-model="bioDialog.bioLinks[index]"
|
||||
size="small"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
show-count
|
||||
size="sm"
|
||||
style="margin-top: 5px">
|
||||
<img :src="getFaviconUrl(link)" style="width: 16px; height: 16px; vertical-align: middle" />
|
||||
<Button variant="outline" @click="bioDialog.bioLinks.splice(index, 1)" />
|
||||
</el-input>
|
||||
<template #leading>
|
||||
<img :src="getFaviconUrl(link)" style="width: 16px; height: 16px; vertical-align: middle" />
|
||||
</template>
|
||||
<template #actions>
|
||||
<Button variant="outline" @click="bioDialog.bioLinks.splice(index, 1)" />
|
||||
</template>
|
||||
</InputGroupAction>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -46,8 +50,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Delete } from '@element-plus/icons-vue';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupAction, InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -11,26 +11,24 @@
|
||||
<template v-if="!hideUserNotes || (hideUserNotes && hideUserMemos)">
|
||||
<span class="name">{{ t('dialog.user.info.note') }}</span>
|
||||
<br />
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="note"
|
||||
:autosize="{ minRows: 6, maxRows: 20 }"
|
||||
:maxlength="256"
|
||||
multiline
|
||||
rows="6"
|
||||
:rows="6"
|
||||
:placeholder="t('dialog.user.info.note_placeholder')"
|
||||
input-class="extra resize-none" />
|
||||
input-class="extra resize-none"
|
||||
show-count />
|
||||
</template>
|
||||
<template v-if="!hideUserMemos || (hideUserNotes && hideUserMemos)">
|
||||
<span class="name">{{ t('dialog.user.info.memo') }}</span>
|
||||
<br />
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="memo"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
:autosize="{ minRows: 2, maxRows: 20 }"
|
||||
:placeholder="t('dialog.user.info.memo_placeholder')"
|
||||
size="small"
|
||||
resize="none"></el-input>
|
||||
input-class="resize-none min-h-0" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -44,7 +42,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
append-to-body>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<span style="font-size: 14px" v-text="previousInstancesUserDialog.userRef.displayName"></span>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="previousInstancesUserDialogTable.filters[0].value"
|
||||
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||
style="display: block; width: 150px"></el-input>
|
||||
style="display: block; width: 150px" />
|
||||
</div>
|
||||
<DataTable :loading="loading" v-bind="previousInstancesUserDialogTable" style="margin-top: 10px">
|
||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||
@@ -78,6 +78,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
width="600px"
|
||||
append-to-body>
|
||||
<div v-loading="pronounsDialog.loading">
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="pronounsDialog.pronouns"
|
||||
:maxlength="32"
|
||||
multiline
|
||||
rows="2"
|
||||
:placeholder="t('dialog.pronouns.pronouns_placeholder')" />
|
||||
:rows="2"
|
||||
:placeholder="t('dialog.pronouns.pronouns_placeholder')"
|
||||
show-count />
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button :disabled="pronounsDialog.loading" @click="savePronouns">
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -41,11 +41,12 @@
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<InputGroupCharCount
|
||||
<InputGroupField
|
||||
v-model="socialStatusDialog.statusDescription"
|
||||
:placeholder="t('dialog.social_status.status_placeholder')"
|
||||
:maxlength="32"
|
||||
clearable
|
||||
show-count
|
||||
class="mt-2.5" />
|
||||
<Collapsible v-model:open="isOpen" class="mt-3 flex w-full flex-col gap-2">
|
||||
<div class="flex items-center justify-between gap-4 px-4">
|
||||
@@ -88,8 +89,8 @@
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ChevronsUpDown } from 'lucide-vue-next';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -20,14 +20,12 @@
|
||||
<span>{{ t('dialog.set_world_tags.enable_debugging') }}</span>
|
||||
</label>
|
||||
<div style="font-size: 12px; margin-top: 10px">{{ t('dialog.set_world_tags.author_tags') }}<br /></div>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="setWorldTagsDialog.authorTags"
|
||||
type="textarea"
|
||||
size="small"
|
||||
show-word-limit
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
:rows="2"
|
||||
placeholder=""
|
||||
style="margin-top: 10px"></el-input>
|
||||
style="margin-top: 10px"
|
||||
input-class="resize-none" />
|
||||
<div style="font-size: 12px; margin-top: 10px">{{ t('dialog.set_world_tags.content_tags') }}<br /></div>
|
||||
<label class="inline-flex items-center gap-2">
|
||||
<Checkbox v-model="setWorldTagsDialog.contentHorror" />
|
||||
@@ -106,6 +104,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
destroy-on-close
|
||||
append-to-body>
|
||||
<div>
|
||||
<el-input
|
||||
<InputGroupAction
|
||||
v-for="(domain, index) in urlList"
|
||||
:key="index"
|
||||
v-model="urlList[index]"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="margin-top: 5px">
|
||||
<Button variant="outline" @click="urlList.splice(index, 1)"></Button>
|
||||
</el-input>
|
||||
<template #actions>
|
||||
<Button variant="outline" @click="urlList.splice(index, 1)"></Button>
|
||||
</template>
|
||||
</InputGroupAction>
|
||||
<Button size="sm" variant="outline" style="margin-top: 5px" @click="urlList.push('')">
|
||||
{{ t('dialog.allowed_video_player_domains.add_domain') }}
|
||||
</Button>
|
||||
@@ -29,6 +31,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupAction } from '@/components/ui/input-group';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -437,15 +437,12 @@
|
||||
<span class="name">
|
||||
{{ t('dialog.world.info.memo') }}
|
||||
</span>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="memo"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:autosize="{ minRows: 1, maxRows: 20 }"
|
||||
:placeholder="t('dialog.world.info.memo_placeholder')"
|
||||
size="small"
|
||||
resize="none"
|
||||
input-class="resize-none min-h-0"
|
||||
@change="onWorldMemoChange" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -769,6 +766,7 @@
|
||||
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
||||
import { Ellipsis, RefreshCcw, Star, Trash2 } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
47
src/components/ui/input-group/InputGroupAction.vue
Normal file
47
src/components/ui/input-group/InputGroupAction.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup>
|
||||
import { useAttrs } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import InputGroupField from './InputGroupField.vue';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
class: { type: null, required: false },
|
||||
inputClass: { type: null, required: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
showPassword: { type: Boolean, default: false },
|
||||
showCount: { type: Boolean, default: false },
|
||||
maxlength: { type: Number, required: false },
|
||||
size: { type: String, default: 'default' }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const attrs = useAttrs();
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emit, {
|
||||
passive: true,
|
||||
defaultValue: props.modelValue
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InputGroupField
|
||||
v-model="modelValue"
|
||||
:class="props.class"
|
||||
:input-class="props.inputClass"
|
||||
:clearable="props.clearable"
|
||||
:show-password="props.showPassword"
|
||||
:show-count="props.showCount"
|
||||
:maxlength="props.maxlength"
|
||||
:size="props.size"
|
||||
v-bind="attrs">
|
||||
<template v-if="$slots.leading" #leading>
|
||||
<slot name="leading" />
|
||||
</template>
|
||||
<template v-if="$slots.actions" #trailing>
|
||||
<slot name="actions" />
|
||||
</template>
|
||||
</InputGroupField>
|
||||
</template>
|
||||
50
src/components/ui/input-group/InputGroupAffix.vue
Normal file
50
src/components/ui/input-group/InputGroupAffix.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup>
|
||||
import { computed, useAttrs, useSlots } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import InputGroupField from './InputGroupField.vue';
|
||||
import { InputGroupText } from '.';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
class: { type: null, required: false },
|
||||
inputClass: { type: null, required: false },
|
||||
prefixText: { type: String, default: '' },
|
||||
suffixText: { type: String, default: '' },
|
||||
clearable: { type: Boolean, default: false },
|
||||
size: { type: String, default: 'default' }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const attrs = useAttrs();
|
||||
const slots = useSlots();
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emit, {
|
||||
passive: true,
|
||||
defaultValue: props.modelValue
|
||||
});
|
||||
|
||||
const hasLeading = computed(() => Boolean(props.prefixText) || Boolean(slots.leading));
|
||||
const hasTrailing = computed(() => Boolean(props.suffixText) || Boolean(slots.trailing));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InputGroupField
|
||||
v-model="modelValue"
|
||||
:class="props.class"
|
||||
:input-class="props.inputClass"
|
||||
:clearable="props.clearable"
|
||||
:size="props.size"
|
||||
v-bind="attrs">
|
||||
<template v-if="hasLeading" #leading>
|
||||
<InputGroupText v-if="props.prefixText">{{ props.prefixText }}</InputGroupText>
|
||||
<slot name="leading" />
|
||||
</template>
|
||||
<template v-if="hasTrailing" #trailing>
|
||||
<slot name="trailing" />
|
||||
<InputGroupText v-if="props.suffixText">{{ props.suffixText }}</InputGroupText>
|
||||
</template>
|
||||
</InputGroupField>
|
||||
</template>
|
||||
@@ -1,81 +0,0 @@
|
||||
<script setup>
|
||||
import { Hash, X } from 'lucide-vue-next';
|
||||
import { computed, useAttrs } from 'vue';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
InputGroupText,
|
||||
InputGroupTextarea
|
||||
} from '.';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
maxlength: { type: Number, required: true },
|
||||
multiline: { type: Boolean, default: false },
|
||||
class: { type: null, required: false },
|
||||
inputClass: { type: null, required: false },
|
||||
countClass: { type: null, required: false },
|
||||
iconClass: { type: null, required: false },
|
||||
clearable: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const attrs = useAttrs();
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emit, {
|
||||
passive: true,
|
||||
defaultValue: props.modelValue
|
||||
});
|
||||
|
||||
const valueLength = computed(() => String(modelValue.value ?? '').length);
|
||||
const remaining = computed(() => Math.max(props.maxlength - valueLength.value, 0));
|
||||
const wrapperClass = computed(() => cn(props.class, attrs.class));
|
||||
const inputClass = computed(() => cn(props.inputClass));
|
||||
const inputAttrs = computed(() => {
|
||||
const { class: _class, style: _style, ...rest } = attrs;
|
||||
return rest;
|
||||
});
|
||||
|
||||
const isDisabled = computed(() => Boolean(inputAttrs.value.disabled));
|
||||
|
||||
function clearValue() {
|
||||
if (isDisabled.value) return;
|
||||
modelValue.value = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InputGroup :class="wrapperClass" :style="attrs.style">
|
||||
<InputGroupAddon v-if="$slots.leading" align="inline-start">
|
||||
<slot name="leading" />
|
||||
</InputGroupAddon>
|
||||
<component
|
||||
:is="props.multiline ? InputGroupTextarea : InputGroupInput"
|
||||
v-model="modelValue"
|
||||
:maxlength="props.maxlength"
|
||||
:class="inputClass"
|
||||
v-bind="inputAttrs" />
|
||||
<InputGroupAddon v-if="$slots.trailing" align="inline-end">
|
||||
<slot name="trailing" />
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon v-if="props.clearable && valueLength > 0" align="inline-end">
|
||||
<InputGroupButton size="icon-xs" :disabled="isDisabled" @click="clearValue">
|
||||
<X class="size-3.5" />
|
||||
<span class="sr-only">Clear</span>
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon :align="props.multiline ? 'block-end' : 'inline-end'" v-if="valueLength > 0">
|
||||
<InputGroupText :class="cn('gap-1 tabular-nums text-xs', props.multiline && 'ml-auto', props.countClass)">
|
||||
<span>{{ valueLength }}</span>
|
||||
<span class="text-muted-foreground/70">/ {{ props.maxlength }}</span>
|
||||
</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</template>
|
||||
124
src/components/ui/input-group/InputGroupField.vue
Normal file
124
src/components/ui/input-group/InputGroupField.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<script setup>
|
||||
import { computed, ref, useAttrs } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { Eye, EyeOff, X } from 'lucide-vue-next';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from '.';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
class: { type: null, required: false },
|
||||
inputClass: { type: null, required: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
showPassword: { type: Boolean, default: false },
|
||||
showCount: { type: Boolean, default: false },
|
||||
maxlength: { type: Number, required: false },
|
||||
type: { type: String, required: false },
|
||||
size: { type: String, default: 'default' }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'input', 'change']);
|
||||
const attrs = useAttrs();
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emit, {
|
||||
passive: true,
|
||||
defaultValue: props.modelValue
|
||||
});
|
||||
|
||||
const reveal = ref(false);
|
||||
const valueLength = computed(() => String(modelValue.value ?? '').length);
|
||||
const maxLength = computed(() => props.maxlength ?? attrs.maxlength);
|
||||
const wrapperClass = computed(() =>
|
||||
cn(props.class, attrs.class, props.size === 'sm' && 'h-8')
|
||||
);
|
||||
const inputClass = computed(() => cn(props.inputClass));
|
||||
|
||||
const inputType = computed(() => {
|
||||
const rawType = props.type ?? attrs.type;
|
||||
if (props.showPassword) {
|
||||
return reveal.value ? 'text' : rawType || 'password';
|
||||
}
|
||||
return rawType;
|
||||
});
|
||||
|
||||
const inputAttrs = computed(() => {
|
||||
const {
|
||||
class: _class,
|
||||
style: _style,
|
||||
type: _type,
|
||||
maxlength: _maxlength,
|
||||
onInput: _onInput,
|
||||
onChange: _onChange,
|
||||
...rest
|
||||
} = attrs;
|
||||
return {
|
||||
...rest,
|
||||
type: inputType.value,
|
||||
maxlength: maxLength.value
|
||||
};
|
||||
});
|
||||
|
||||
const isDisabled = computed(() => Boolean(inputAttrs.value.disabled));
|
||||
const showCount = computed(() => Boolean(maxLength.value) && props.showCount);
|
||||
|
||||
function clearValue() {
|
||||
if (isDisabled.value) return;
|
||||
modelValue.value = '';
|
||||
emit('input', '');
|
||||
emit('change', '');
|
||||
}
|
||||
|
||||
function toggleReveal() {
|
||||
if (isDisabled.value) return;
|
||||
reveal.value = !reveal.value;
|
||||
}
|
||||
|
||||
function handleInput(event) {
|
||||
const value = event?.target?.value ?? '';
|
||||
emit('input', value);
|
||||
}
|
||||
|
||||
function handleChange(event) {
|
||||
const value = event?.target?.value ?? '';
|
||||
emit('change', value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InputGroup :class="wrapperClass" :style="attrs.style" :data-disabled="isDisabled ? 'true' : undefined">
|
||||
<InputGroupAddon v-if="$slots.leading" align="inline-start">
|
||||
<slot name="leading" />
|
||||
</InputGroupAddon>
|
||||
<InputGroupInput
|
||||
v-model="modelValue"
|
||||
:class="inputClass"
|
||||
v-bind="inputAttrs"
|
||||
@input="handleInput"
|
||||
@change="handleChange" />
|
||||
<InputGroupAddon v-if="$slots.trailing" align="inline-end">
|
||||
<slot name="trailing" />
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon v-if="props.showPassword" align="inline-end">
|
||||
<InputGroupButton size="icon-xs" :disabled="isDisabled" @click="toggleReveal">
|
||||
<Eye v-if="!reveal" class="size-3.5" />
|
||||
<EyeOff v-else class="size-3.5" />
|
||||
<span class="sr-only">Toggle password</span>
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon v-if="props.clearable && valueLength > 0" align="inline-end">
|
||||
<InputGroupButton size="icon-xs" :disabled="isDisabled" @click="clearValue">
|
||||
<X class="size-3.5" />
|
||||
<span class="sr-only">Clear</span>
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon v-if="showCount && valueLength > 0" align="inline-end">
|
||||
<InputGroupText class="gap-1 tabular-nums text-xs">
|
||||
<span>{{ valueLength }}</span>
|
||||
<span class="text-muted-foreground/70">/ {{ maxLength }}</span>
|
||||
</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</template>
|
||||
42
src/components/ui/input-group/InputGroupSearch.vue
Normal file
42
src/components/ui/input-group/InputGroupSearch.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<script setup>
|
||||
import { useAttrs } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { Search } from 'lucide-vue-next';
|
||||
|
||||
import InputGroupField from './InputGroupField.vue';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
class: { type: null, required: false },
|
||||
inputClass: { type: null, required: false },
|
||||
clearable: { type: Boolean, default: true },
|
||||
size: { type: String, default: 'default' }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const attrs = useAttrs();
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emit, {
|
||||
passive: true,
|
||||
defaultValue: props.modelValue
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InputGroupField
|
||||
v-model="modelValue"
|
||||
:class="props.class"
|
||||
:input-class="props.inputClass"
|
||||
:clearable="props.clearable"
|
||||
:size="props.size"
|
||||
v-bind="attrs">
|
||||
<template #leading>
|
||||
<Search class="size-4" />
|
||||
</template>
|
||||
<template v-if="$slots.trailing" #trailing>
|
||||
<slot name="trailing" />
|
||||
</template>
|
||||
</InputGroupField>
|
||||
</template>
|
||||
144
src/components/ui/input-group/InputGroupTextareaField.vue
Normal file
144
src/components/ui/input-group/InputGroupTextareaField.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, ref, useAttrs, watch } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { X } from 'lucide-vue-next';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupTextarea } from '.';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
class: { type: null, required: false },
|
||||
inputClass: { type: null, required: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
showCount: { type: Boolean, default: false },
|
||||
maxlength: { type: Number, required: false },
|
||||
autosize: { type: [Boolean, Object], default: false }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'input', 'change']);
|
||||
const attrs = useAttrs();
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emit, {
|
||||
passive: true,
|
||||
defaultValue: props.modelValue
|
||||
});
|
||||
|
||||
const textareaRef = ref(null);
|
||||
const valueLength = computed(() => String(modelValue.value ?? '').length);
|
||||
const maxLength = computed(() => props.maxlength ?? attrs.maxlength);
|
||||
const wrapperClass = computed(() => cn(props.class, attrs.class));
|
||||
const inputClass = computed(() => cn(props.inputClass));
|
||||
const showCount = computed(() => Boolean(maxLength.value) && props.showCount);
|
||||
const autosizeConfig = computed(() => {
|
||||
if (!props.autosize) return null;
|
||||
return typeof props.autosize === 'object' ? props.autosize : {};
|
||||
});
|
||||
|
||||
const inputAttrs = computed(() => {
|
||||
const {
|
||||
class: _class,
|
||||
style: _style,
|
||||
maxlength: _maxlength,
|
||||
onInput: _onInput,
|
||||
onChange: _onChange,
|
||||
...rest
|
||||
} = attrs;
|
||||
return {
|
||||
...rest,
|
||||
maxlength: maxLength.value
|
||||
};
|
||||
});
|
||||
|
||||
const isDisabled = computed(() => Boolean(inputAttrs.value.disabled));
|
||||
|
||||
function resolveTextareaEl() {
|
||||
const instance = textareaRef.value;
|
||||
if (!instance) return null;
|
||||
return instance.$el ?? instance;
|
||||
}
|
||||
|
||||
function resizeTextarea() {
|
||||
if (!autosizeConfig.value) return;
|
||||
const el = resolveTextareaEl();
|
||||
if (!el) return;
|
||||
const computedStyle = window.getComputedStyle(el);
|
||||
const lineHeight = parseFloat(computedStyle.lineHeight) || 16;
|
||||
const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
|
||||
const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
|
||||
const minRows = autosizeConfig.value.minRows ?? Number(attrs.rows) || 1;
|
||||
const maxRows = autosizeConfig.value.maxRows ?? Number.POSITIVE_INFINITY;
|
||||
const minHeight = lineHeight * minRows + paddingTop + paddingBottom;
|
||||
const maxHeight = lineHeight * maxRows + paddingTop + paddingBottom;
|
||||
|
||||
el.style.height = 'auto';
|
||||
const nextHeight = Math.min(maxHeight, Math.max(el.scrollHeight, minHeight));
|
||||
el.style.height = `${nextHeight}px`;
|
||||
}
|
||||
|
||||
function clearValue() {
|
||||
if (isDisabled.value) return;
|
||||
modelValue.value = '';
|
||||
emit('input', '');
|
||||
emit('change', '');
|
||||
nextTick(resizeTextarea);
|
||||
}
|
||||
|
||||
function handleInput(event) {
|
||||
const value = event?.target?.value ?? '';
|
||||
emit('input', value);
|
||||
resizeTextarea();
|
||||
}
|
||||
|
||||
function handleChange(event) {
|
||||
const value = event?.target?.value ?? '';
|
||||
emit('change', value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (autosizeConfig.value) {
|
||||
nextTick(resizeTextarea);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => modelValue.value,
|
||||
() => {
|
||||
if (autosizeConfig.value) {
|
||||
nextTick(resizeTextarea);
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InputGroup :class="wrapperClass" :style="attrs.style" :data-disabled="isDisabled ? 'true' : undefined">
|
||||
<InputGroupAddon v-if="$slots.leading" align="block-start">
|
||||
<slot name="leading" />
|
||||
</InputGroupAddon>
|
||||
<InputGroupTextarea
|
||||
ref="textareaRef"
|
||||
v-model="modelValue"
|
||||
:class="inputClass"
|
||||
v-bind="inputAttrs"
|
||||
@input="handleInput"
|
||||
@change="handleChange" />
|
||||
<InputGroupAddon v-if="$slots.trailing" align="block-end">
|
||||
<slot name="trailing" />
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon v-if="props.clearable && valueLength > 0" align="inline-end">
|
||||
<InputGroupButton size="icon-xs" :disabled="isDisabled" @click="clearValue">
|
||||
<X class="size-3.5" />
|
||||
<span class="sr-only">Clear</span>
|
||||
</InputGroupButton>
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon v-if="showCount && valueLength > 0" align="block-end">
|
||||
<InputGroupText class="gap-1 tabular-nums text-xs">
|
||||
<span>{{ valueLength }}</span>
|
||||
<span class="text-muted-foreground/70">/ {{ maxLength }}</span>
|
||||
</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</template>
|
||||
@@ -6,7 +6,11 @@ export { default as InputGroupButton } from './InputGroupButton.vue';
|
||||
export { default as InputGroupInput } from './InputGroupInput.vue';
|
||||
export { default as InputGroupText } from './InputGroupText.vue';
|
||||
export { default as InputGroupTextarea } from './InputGroupTextarea.vue';
|
||||
export { default as InputGroupCharCount } from './InputGroupCharCount.vue';
|
||||
export { default as InputGroupField } from './InputGroupField.vue';
|
||||
export { default as InputGroupTextareaField } from './InputGroupTextareaField.vue';
|
||||
export { default as InputGroupSearch } from './InputGroupSearch.vue';
|
||||
export { default as InputGroupAffix } from './InputGroupAffix.vue';
|
||||
export { default as InputGroupAction } from './InputGroupAction.vue';
|
||||
|
||||
export const inputGroupAddonVariants = cva(
|
||||
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
|
||||
|
||||
@@ -27,9 +27,8 @@
|
||||
</Select>
|
||||
</div>
|
||||
<div class="favorites-toolbar__right">
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="avatarFavoriteSearch"
|
||||
clearable
|
||||
class="favorites-toolbar__search"
|
||||
:placeholder="t('view.favorite.avatars.search')"
|
||||
@input="searchAvatarFavorites" />
|
||||
@@ -280,11 +279,11 @@
|
||||
<span>{{ t('view.favorite.avatars.new_group') }}</span>
|
||||
</div>
|
||||
</TooltipWrapper>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-else
|
||||
ref="newLocalGroupInput"
|
||||
v-model="newLocalGroupName"
|
||||
size="small"
|
||||
size="sm"
|
||||
class="group-item__input"
|
||||
:placeholder="t('view.favorite.avatars.new_group')"
|
||||
@keyup.enter="handleLocalGroupCreationConfirm"
|
||||
@@ -515,6 +514,7 @@
|
||||
import { MoreFilled, Plus, Refresh } from '@element-plus/icons-vue';
|
||||
import { Ellipsis, RefreshCcw } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField, InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { Loader } from 'lucide-vue-next';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -27,9 +27,8 @@
|
||||
</Select>
|
||||
</div>
|
||||
<div class="favorites-toolbar__right">
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="friendFavoriteSearch"
|
||||
clearable
|
||||
class="favorites-toolbar__search"
|
||||
:placeholder="t('view.favorite.worlds.search')"
|
||||
@input="searchFriendFavorites" />
|
||||
@@ -297,6 +296,7 @@
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue';
|
||||
import { MoreFilled, Refresh } from '@element-plus/icons-vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Ellipsis } from 'lucide-vue-next';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
|
||||
@@ -27,9 +27,8 @@
|
||||
</Select>
|
||||
</div>
|
||||
<div class="favorites-toolbar__right">
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="worldFavoriteSearch"
|
||||
clearable
|
||||
class="favorites-toolbar__search"
|
||||
:placeholder="t('view.favorite.worlds.search')"
|
||||
@input="searchWorldFavorites" />
|
||||
@@ -266,11 +265,11 @@
|
||||
<el-icon><Plus /></el-icon>
|
||||
<span>{{ t('view.favorite.worlds.new_group') }}</span>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-else
|
||||
ref="newLocalGroupInput"
|
||||
v-model="newLocalGroupName"
|
||||
size="small"
|
||||
size="sm"
|
||||
class="group-item__input"
|
||||
:placeholder="t('view.favorite.worlds.new_group')"
|
||||
@keyup.enter="handleLocalGroupCreationConfirm"
|
||||
@@ -428,6 +427,7 @@
|
||||
import { MoreFilled, Plus, Refresh } from '@element-plus/icons-vue';
|
||||
import { Ellipsis, RefreshCcw } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField, InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -47,15 +47,13 @@
|
||||
</Select>
|
||||
</div>
|
||||
<br />
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="avatarExportContent"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
@click="handleCopyAvatarExportData"></el-input>
|
||||
input-class="resize-none"
|
||||
@click="handleCopyAvatarExportData" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@@ -63,6 +61,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -20,13 +20,11 @@
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="avatarImportDialog.input"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="10"
|
||||
resize="none"
|
||||
style="margin-top: 10px"></el-input>
|
||||
style="margin-top: 10px"
|
||||
input-class="resize-none" />
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 5px">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -163,6 +161,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { Trash2 } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -21,15 +21,13 @@
|
||||
|
||||
<br />
|
||||
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="friendExportContent"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
@click="handleCopyFriendExportData"></el-input>
|
||||
input-class="resize-none"
|
||||
@click="handleCopyFriendExportData" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@@ -37,6 +35,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -20,13 +20,11 @@
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="friendImportDialog.input"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="10"
|
||||
resize="none"
|
||||
style="margin-top: 10px" />
|
||||
style="margin-top: 10px"
|
||||
input-class="resize-none" />
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 5px">
|
||||
<div>
|
||||
<Select
|
||||
@@ -121,6 +119,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { Trash2 } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -44,15 +44,13 @@
|
||||
|
||||
<br />
|
||||
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="worldExportContent"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
@click="handleCopyWorldExportData"></el-input>
|
||||
input-class="resize-none"
|
||||
@click="handleCopyWorldExportData" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@@ -60,6 +58,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -21,13 +21,11 @@
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="worldImportDialog.input"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="10"
|
||||
resize="none"
|
||||
style="margin-top: 10px"></el-input>
|
||||
style="margin-top: 10px"
|
||||
input-class="resize-none" />
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 5px">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -165,6 +163,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { Trash2 } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
:label="t('view.feed.filters.' + type)"
|
||||
:value="type"></el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="feedTable.search"
|
||||
:placeholder="t('view.feed.search_placeholder')"
|
||||
clearable
|
||||
style="flex: 0.4; margin-left: 10px"
|
||||
@keyup.enter="feedTableLookup"
|
||||
@change="feedTableLookup"></el-input>
|
||||
@change="feedTableLookup" />
|
||||
</div>
|
||||
</template>
|
||||
</DataTableLayout>
|
||||
@@ -46,6 +46,7 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAppearanceSettingsStore, useFeedStore, useVrcxStore } from '../../stores';
|
||||
import { InputGroupField } from '../../components/ui/input-group';
|
||||
import { DataTableLayout } from '../../components/ui/data-table';
|
||||
import { Switch } from '../../components/ui/switch';
|
||||
import { columns as baseColumns } from './columns.jsx';
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
:label="type"
|
||||
:value="type"></el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="friendsListSearch"
|
||||
:placeholder="t('view.friend_list.search_placeholder')"
|
||||
clearable
|
||||
style="width: 250px"
|
||||
@change="friendsListSearchChange"></el-input>
|
||||
@change="friendsListSearchChange" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div v-if="friendsListBulkUnfriendMode" class="inline-block mr-10">
|
||||
@@ -261,6 +261,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
:label="t('view.friend_log.filters.' + type)"
|
||||
:value="type" />
|
||||
</el-select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="friendLogTable.filters[1].value"
|
||||
:placeholder="t('view.friend_log.search_placeholder')"
|
||||
style="flex: 0.4; margin-left: 10px" />
|
||||
@@ -48,6 +48,7 @@
|
||||
|
||||
import { useAppearanceSettingsStore, useFriendStore, useVrcxStore } from '../../stores';
|
||||
import { DataTableLayout } from '../../components/ui/data-table';
|
||||
import { InputGroupField } from '../../components/ui/input-group';
|
||||
import { createColumns } from './columns.jsx';
|
||||
import { database } from '../../service/database';
|
||||
import { removeFromArray } from '../../shared/utils';
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
<div v-if="settingsReady" class="friend-view__toolbar">
|
||||
<el-segmented v-model="activeSegment" :options="segmentedOptions" />
|
||||
<div class="friend-view__actions">
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="searchTerm"
|
||||
class="friend-view__search"
|
||||
:prefix-icon="Search"
|
||||
clearable
|
||||
placeholder="Search Friend"></el-input>
|
||||
placeholder="Search Friend" />
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div>
|
||||
@@ -163,8 +161,9 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { Loading, Search } from '@element-plus/icons-vue';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { Settings } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -36,13 +36,13 @@
|
||||
:label="t('view.game_log.filters.' + type)"
|
||||
:value="type"></el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="gameLogTable.search"
|
||||
:placeholder="t('view.game_log.search_placeholder')"
|
||||
clearable
|
||||
style="flex: 0.4; margin-left: 10px"
|
||||
@keyup.enter="gameLogTableLookup"
|
||||
@change="gameLogTableLookup"></el-input>
|
||||
@change="gameLogTableLookup" />
|
||||
</div>
|
||||
</template>
|
||||
</DataTableLayout>
|
||||
@@ -60,6 +60,7 @@
|
||||
|
||||
import { useAppearanceSettingsStore, useGameLogStore, useVrcxStore } from '../../stores';
|
||||
import { DataTableLayout } from '../../components/ui/data-table';
|
||||
import { InputGroupField } from '../../components/ui/input-group';
|
||||
import { createColumns } from './columns.jsx';
|
||||
import { database } from '../../service/database';
|
||||
import { removeFromArray } from '../../shared/utils';
|
||||
|
||||
@@ -26,24 +26,24 @@
|
||||
prop="username"
|
||||
required
|
||||
style="display: block">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="loginForm.username"
|
||||
name="username"
|
||||
:placeholder="t('view.login.field.username')"
|
||||
clearable></el-input>
|
||||
clearable />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="t('view.login.field.password')"
|
||||
prop="password"
|
||||
required
|
||||
style="display: block; margin-top: 10px">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
name="password"
|
||||
:placeholder="t('view.login.field.password')"
|
||||
clearable
|
||||
show-password></el-input>
|
||||
show-password />
|
||||
</el-form-item>
|
||||
<label class="inline-flex items-center gap-2 mr-2">
|
||||
<Checkbox v-model="loginForm.saveCredentials" />
|
||||
@@ -58,22 +58,22 @@
|
||||
:label="t('view.login.field.endpoint')"
|
||||
prop="endpoint"
|
||||
style="margin-top: 10px">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="loginForm.endpoint"
|
||||
name="endpoint"
|
||||
:placeholder="AppDebug.endpointDomainVrchat"
|
||||
clearable></el-input>
|
||||
clearable />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="enableCustomEndpoint"
|
||||
:label="t('view.login.field.websocket')"
|
||||
prop="websocket"
|
||||
style="margin-top: 10px">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="loginForm.websocket"
|
||||
name="websocket"
|
||||
:placeholder="AppDebug.websocketDomainVrchat"
|
||||
clearable></el-input>
|
||||
clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<Button class="mt-2" type="submit" size="lg" style="width: 100%">{{
|
||||
@@ -151,6 +151,7 @@
|
||||
import { CircleArrowDown, Route } from 'lucide-vue-next';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
:label="t('view.moderation.filters.' + item)"
|
||||
:value="item" />
|
||||
</el-select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="playerModerationTable.filters[1].value"
|
||||
:placeholder="t('view.moderation.search_placeholder')"
|
||||
class="filter-input" />
|
||||
@@ -44,6 +44,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
:label="t('view.notification.filters.' + type)"
|
||||
:value="type" />
|
||||
</el-select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="notificationTable.filters[1].value"
|
||||
:placeholder="t('view.notification.search_placeholder')"
|
||||
clearable
|
||||
@@ -75,6 +75,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
<div style="font-size: 12px">
|
||||
<span>{{ t('dialog.edit_send_invite_response_message.description') }}</span>
|
||||
</div>
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="editAndSendInviteResponseDialog.newMessage"
|
||||
:maxlength="64"
|
||||
multiline
|
||||
rows="2"
|
||||
:rows="2"
|
||||
class="mt-2.5"
|
||||
placeholder="" />
|
||||
placeholder=""
|
||||
show-count />
|
||||
<template #footer>
|
||||
<Button variant="secondary" class="mr-2" @click="cancelEditAndSendInviteResponse">{{
|
||||
t('dialog.edit_send_invite_response_message.cancel')
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -156,6 +156,7 @@ export const createColumns = ({
|
||||
class={[
|
||||
'x-user-status',
|
||||
'shrink-0',
|
||||
'mr-1',
|
||||
status ? statusClass(status) : null
|
||||
]}
|
||||
></i>
|
||||
@@ -339,7 +340,7 @@ export const createColumns = ({
|
||||
const userRef = row.original?.ref;
|
||||
const langs = userRef?.$languages ?? [];
|
||||
return (
|
||||
<div>
|
||||
<div class="flex items-center gap-0.5">
|
||||
{langs.map((item) => (
|
||||
<TooltipWrapper
|
||||
key={item.key}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
}}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="photonEventTableFilter"
|
||||
:placeholder="t('view.player_list.photon.search_placeholder')"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
@input="photonEventTableFilterChange"></el-input>
|
||||
@input="photonEventTableFilterChange" />
|
||||
<Button variant="outline" @click="emitShowChatboxBlacklist">{{
|
||||
t('view.player_list.photon.chatbox_blacklist')
|
||||
}}</Button>
|
||||
@@ -390,6 +390,7 @@
|
||||
<script setup>
|
||||
import { ArrowRight, Download } from '@element-plus/icons-vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
width="600px">
|
||||
<div v-if="chatboxBlacklistDialog.visible" v-loading="chatboxBlacklistDialog.loading">
|
||||
<h2>{{ t('dialog.chatbox_blacklist.keyword_blacklist') }}</h2>
|
||||
<el-input
|
||||
<InputGroupAction
|
||||
v-for="(item, index) in chatboxBlacklist"
|
||||
:key="index"
|
||||
v-model="chatboxBlacklist[index]"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="margin-top: 5px"
|
||||
@change="saveChatboxBlacklist">
|
||||
<template #append>
|
||||
<template #actions>
|
||||
<Button
|
||||
variant="outline"
|
||||
@click="
|
||||
@@ -22,7 +22,7 @@
|
||||
">
|
||||
</Button>
|
||||
</template>
|
||||
</el-input>
|
||||
</InputGroupAction>
|
||||
<Button size="sm" variant="outline" style="margin-top: 5px" @click="chatboxBlacklist.push('')">
|
||||
{{ t('dialog.chatbox_blacklist.add_item') }}
|
||||
</Button>
|
||||
@@ -56,6 +56,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupAction } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="x-container">
|
||||
<div style="margin: 0 0 10px; display: flex; align-items: center">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
:model-value="searchText"
|
||||
:placeholder="t('view.search.search_placeholder')"
|
||||
style="flex: 1"
|
||||
clearable
|
||||
@input="updateSearchText"
|
||||
@keyup.enter="search"></el-input>
|
||||
@keyup.enter="search" />
|
||||
<TooltipWrapper side="bottom" :content="t('view.search.clear_results_tooltip')">
|
||||
<Button class="rounded-full mr-2" size="icon-sm" variant="ghost" @click="handleClearSearch"
|
||||
><Trash2
|
||||
@@ -347,6 +347,7 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ButtonGroup } from '@/components/ui/button-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { Trash2 } from 'lucide-vue-next';
|
||||
import { ref } from 'vue';
|
||||
|
||||
@@ -256,12 +256,12 @@
|
||||
:value="isTestTTSVisible"
|
||||
@change="isTestTTSVisible = !isTestTTSVisible" />
|
||||
<div v-if="isTestTTSVisible" style="margin-top: 5px">
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="notificationTTSTest"
|
||||
type="textarea"
|
||||
:placeholder="t('view.settings.notifications.notifications.text_to_speech.tts_test_placeholder')"
|
||||
:rows="1"
|
||||
style="width: 175px; display: inline-block"></el-input>
|
||||
style="width: 175px; display: inline-block"
|
||||
input-class="resize-none min-h-0" />
|
||||
<Button size="sm" variant="outline" style="margin-left: 10px" @click="testNotificationTTS">{{
|
||||
t('view.settings.notifications.notifications.text_to_speech.play')
|
||||
}}</Button>
|
||||
@@ -276,6 +276,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -6,15 +6,19 @@
|
||||
width="600px"
|
||||
@close="closeDialog">
|
||||
<div>
|
||||
<el-input
|
||||
<InputGroupAction
|
||||
v-for="(provider, index) in avatarRemoteDatabaseProviderList"
|
||||
:key="index"
|
||||
v-model="avatarRemoteDatabaseProviderList[index]"
|
||||
size="small"
|
||||
size="sm"
|
||||
style="margin-top: 5px"
|
||||
@change="saveAvatarProviderList">
|
||||
<Button variant="outline" size="icon" @click="removeAvatarProvider(provider)"><Trash2 /></Button>
|
||||
</el-input>
|
||||
<template #actions>
|
||||
<Button variant="outline" size="icon" @click="removeAvatarProvider(provider)">
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</template>
|
||||
</InputGroupAction>
|
||||
|
||||
<Button size="sm" style="margin-top: 5px" @click="avatarRemoteDatabaseProviderList.push('')">
|
||||
{{ t('dialog.avatar_database_provider.add_provider') }}
|
||||
@@ -25,6 +29,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupAction } from '@/components/ui/input-group';
|
||||
import { Trash2 } from 'lucide-vue-next';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -13,28 +13,25 @@
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="launchOptionsDialog.launchArguments"
|
||||
type="textarea"
|
||||
size="small"
|
||||
show-word-limit
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
:rows="2"
|
||||
placeholder=""
|
||||
style="margin-top: 10px">
|
||||
</el-input>
|
||||
style="margin-top: 10px"
|
||||
input-class="resize-none" />
|
||||
|
||||
<template v-if="!isLinux">
|
||||
<div style="font-size: 12px; margin-top: 10px">
|
||||
{{ t('dialog.launch_options.path_override') }}
|
||||
</div>
|
||||
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="launchOptionsDialog.vrcLaunchPathOverride"
|
||||
type="textarea"
|
||||
placeholder="C:\Program Files (x86)\Steam\steamapps\common\VRChat"
|
||||
:rows="1"
|
||||
style="display: block; margin-top: 10px">
|
||||
</el-input>
|
||||
style="display: block; margin-top: 10px"
|
||||
input-class="resize-none min-h-0" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
@@ -63,6 +60,7 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -6,24 +6,22 @@
|
||||
:close-on-click-modal="false"
|
||||
:title="t('dialog.primary_password.header')"
|
||||
width="400px">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="enablePrimaryPasswordDialog.password"
|
||||
:placeholder="t('dialog.primary_password.password_placeholder')"
|
||||
type="password"
|
||||
size="small"
|
||||
size="sm"
|
||||
maxlength="32"
|
||||
show-password
|
||||
autofocus>
|
||||
</el-input>
|
||||
<el-input
|
||||
autofocus />
|
||||
<InputGroupField
|
||||
v-model="enablePrimaryPasswordDialog.rePassword"
|
||||
:placeholder="t('dialog.primary_password.re_input_placeholder')"
|
||||
type="password"
|
||||
style="margin-top: 5px"
|
||||
size="small"
|
||||
size="sm"
|
||||
maxlength="32"
|
||||
show-password>
|
||||
</el-input>
|
||||
show-password />
|
||||
<template #footer>
|
||||
<Button
|
||||
:disabled="
|
||||
@@ -39,6 +37,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -44,10 +44,9 @@
|
||||
<template v-if="form.translationApiType === 'google'">
|
||||
<el-form label-position="top" label-width="120px" size="small">
|
||||
<el-form-item :label="t('dialog.translation_api.description')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="form.translationApiKey"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="AIzaSy..."
|
||||
clearable />
|
||||
@@ -58,29 +57,27 @@
|
||||
<template v-if="form.translationApiType === 'openai'">
|
||||
<el-form label-position="top" label-width="120px" size="small">
|
||||
<el-form-item :label="t('dialog.translation_api.openai.endpoint')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="form.translationApiEndpoint"
|
||||
placeholder="https://api.openai.com/v1/chat/completions"
|
||||
clearable
|
||||
textarea />
|
||||
clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('dialog.translation_api.openai.api_key')">
|
||||
<el-input
|
||||
<InputGroupField
|
||||
v-model="form.translationApiKey"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="sk-..."
|
||||
clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('dialog.translation_api.openai.model')">
|
||||
<el-input v-model="form.translationApiModel" clearable />
|
||||
<InputGroupField v-model="form.translationApiModel" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('dialog.translation_api.openai.prompt_optional')">
|
||||
<el-input v-model="form.translationApiPrompt" type="textarea" :rows="3" clearable />
|
||||
<InputGroupTextareaField v-model="form.translationApiPrompt" :rows="3" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -118,6 +115,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { reactive, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -46,23 +46,24 @@
|
||||
<div v-for="(item, value) in VRChatConfigList" :key="value" style="display: block; margin-top: 10px">
|
||||
<span style="word-break: keep-all">{{ item.name }}:</span>
|
||||
<div style="display: flex">
|
||||
<el-input
|
||||
<InputGroupAction
|
||||
v-model="VRChatConfigFile[value]"
|
||||
:placeholder="item.default"
|
||||
size="small"
|
||||
size="sm"
|
||||
:type="item.type ? item.type : 'text'"
|
||||
:min="item.min"
|
||||
:max="item.max"
|
||||
@input="refreshDialogValues"
|
||||
style="flex: 1; margin-top: 5px">
|
||||
<template #append>
|
||||
<template #actions>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
v-if="item.folderBrowser"
|
||||
@click="openConfigFolderBrowser(value)"></Button>
|
||||
@click="openConfigFolderBrowser(value)">
|
||||
</Button>
|
||||
</template>
|
||||
</el-input>
|
||||
</InputGroupAction>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -172,6 +173,7 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupAction } from '@/components/ui/input-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
@close="closeDialog">
|
||||
<div style="font-size: 12px">{{ t('dialog.youtube_api.description') }} <br /></div>
|
||||
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="youTubeApiKey"
|
||||
:placeholder="t('dialog.youtube_api.placeholder')"
|
||||
:maxlength="39"
|
||||
multiline
|
||||
rows="2"
|
||||
class="mt-2.5" />
|
||||
:rows="2"
|
||||
class="mt-2.5"
|
||||
show-count />
|
||||
|
||||
<template #footer>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -418,15 +418,13 @@
|
||||
{{ t('dialog.gallery_icons.upload') }}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<el-input
|
||||
type="textarea"
|
||||
<InputGroupTextareaField
|
||||
v-model="printUploadNote"
|
||||
size="small"
|
||||
:rows="1"
|
||||
resize="none"
|
||||
maxlength="32"
|
||||
style="margin-left: 10px; width: 300px"
|
||||
:placeholder="t('dialog.gallery_icons.note')"></el-input>
|
||||
:placeholder="t('dialog.gallery_icons.note')"
|
||||
input-class="resize-none min-h-0" />
|
||||
<label class="inline-flex items-center gap-2" style="margin-left: 10px; margin-right: 10px">
|
||||
<Checkbox v-model="printCropBorder" />
|
||||
<span>{{ t('dialog.gallery_icons.crop_print_border') }}</span>
|
||||
@@ -556,6 +554,7 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { Maximize2, Trash2 } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { ButtonGroup } from '@/components/ui/button-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
@close="closeDialog">
|
||||
<div style="font-size: 12px">
|
||||
<span>{{ t('dialog.edit_invite_message.description') }}</span>
|
||||
<InputGroupCharCount v-model="message" :maxlength="64" multiline rows="2" class="mt-2.5" placeholder="" />
|
||||
<InputGroupTextareaField
|
||||
v-model="message"
|
||||
:maxlength="64"
|
||||
:rows="2"
|
||||
class="mt-2.5"
|
||||
placeholder=""
|
||||
show-count />
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button variant="secondary" class="mr-2" @click="closeDialog">{{
|
||||
@@ -21,7 +27,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<el-dialog v-model="isVisible" :title="t('dialog.export_own_avatars.header')" width="650px">
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="exportAvatarsListCsv"
|
||||
v-loading="loading"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
input-class="resize-none"
|
||||
@click="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
@@ -16,6 +14,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAvatarStore, useUserStore } from '../../../stores';
|
||||
|
||||
@@ -8,20 +8,19 @@
|
||||
<div style="font-size: 12px">
|
||||
{{ t('dialog.discord_names.description') }}
|
||||
</div>
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="discordNamesContent"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px" />
|
||||
style="margin-top: 15px"
|
||||
input-class="resize-none" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
@@ -2,25 +2,21 @@
|
||||
<el-dialog :title="t('dialog.export_friends_list.header')" v-model="isVisible" width="650px">
|
||||
<el-tabs>
|
||||
<el-tab-pane :label="t('dialog.export_friends_list.csv')">
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="exportFriendsListCsv"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
input-class="resize-none"
|
||||
@click="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="t('dialog.export_friends_list.json')">
|
||||
<el-input
|
||||
<InputGroupTextareaField
|
||||
v-model="exportFriendsListJson"
|
||||
type="textarea"
|
||||
size="small"
|
||||
:rows="15"
|
||||
resize="none"
|
||||
readonly
|
||||
style="margin-top: 15px"
|
||||
input-class="resize-none"
|
||||
@click="$event.target.tagName === 'TEXTAREA' && $event.target.select()" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@@ -30,6 +26,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
@@ -76,12 +76,10 @@
|
||||
</div>
|
||||
<div v-else key="grid" class="grid-view">
|
||||
<div class="search-container">
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="searchQuery"
|
||||
size="sm"
|
||||
:placeholder="t('dialog.group_calendar.search_placeholder')"
|
||||
clearable
|
||||
size="small"
|
||||
prefix-:icon="Search"
|
||||
class="search-input" />
|
||||
</div>
|
||||
|
||||
@@ -127,6 +125,7 @@
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { ArrowRight } from '@element-plus/icons-vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@@ -70,12 +70,12 @@
|
||||
|
||||
<el-table-column :label="t('table.import.note')" prop="memo">
|
||||
<template #default="{ row }">
|
||||
<InputGroupCharCount
|
||||
<InputGroupTextareaField
|
||||
v-model="row.memo"
|
||||
:maxlength="256"
|
||||
multiline
|
||||
rows="2"
|
||||
input-class="min-h-0 py-1 resize-none" />
|
||||
:rows="2"
|
||||
input-class="min-h-0 py-1 resize-none"
|
||||
show-count />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupCharCount } from '@/components/ui/input-group';
|
||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -56,10 +56,9 @@
|
||||
|
||||
<div class="flex items-center">
|
||||
<!-- Search bar input -->
|
||||
<el-input
|
||||
<InputGroupSearch
|
||||
v-model="screenshotMetadataDialog.search"
|
||||
placeholder="Search"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@input="screenshotMetadataSearch" />
|
||||
<!-- Search type dropdown -->
|
||||
@@ -167,6 +166,7 @@
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupSearch } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
Reference in New Issue
Block a user