Мобильная версия
1. Адаптация и разработка мобильного варианта.
This commit is contained in:
@@ -1,41 +1,82 @@
|
||||
<template>
|
||||
<div class="comment-form">
|
||||
<!-- Индикатор "ответ на" -->
|
||||
<div v-if="replyingTo" class="reply-indicator">
|
||||
<i data-lucide="corner-down-right"></i>
|
||||
<span>Ответ для <strong>{{ replyingTo.author_name }}</strong></span>
|
||||
<button class="reply-cancel" @click="$emit('cancel-reply')" title="Отменить">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<FormField :label="replyingTo ? 'Ваш ответ' : 'Новый комментарий'">
|
||||
<template #actions>
|
||||
<div class="format-buttons">
|
||||
<button type="button" class="format-btn" @mousedown.prevent="applyFormat('bold')" title="Жирный">
|
||||
<i data-lucide="bold"></i>
|
||||
</button>
|
||||
<button type="button" class="format-btn" @mousedown.prevent="applyFormat('italic')" title="Курсив">
|
||||
<i data-lucide="italic"></i>
|
||||
</button>
|
||||
<button type="button" class="format-btn" @mousedown.prevent="applyFormat('underline')" title="Подчёркивание">
|
||||
<i data-lucide="underline"></i>
|
||||
</button>
|
||||
<button type="button" class="format-btn" @mousedown.prevent="triggerFileInput" title="Прикрепить файл">
|
||||
<i data-lucide="paperclip"></i>
|
||||
<!-- Desktop: Inline форма -->
|
||||
<template v-if="!isMobile">
|
||||
<!-- Индикатор "ответ на" -->
|
||||
<div v-if="replyingTo" class="reply-indicator">
|
||||
<i data-lucide="corner-down-right"></i>
|
||||
<span>Ответ для <strong>{{ replyingTo.author_name }}</strong></span>
|
||||
<button class="reply-cancel" @click="$emit('cancel-reply')" title="Отменить">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<FormField :label="replyingTo ? 'Ваш ответ' : 'Новый комментарий'">
|
||||
<template #actions>
|
||||
<div class="format-buttons">
|
||||
<button type="button" class="format-btn" @mousedown.prevent="applyFormat('bold')" title="Жирный">
|
||||
<i data-lucide="bold"></i>
|
||||
</button>
|
||||
<button type="button" class="format-btn" @mousedown.prevent="applyFormat('italic')" title="Курсив">
|
||||
<i data-lucide="italic"></i>
|
||||
</button>
|
||||
<button type="button" class="format-btn" @mousedown.prevent="applyFormat('underline')" title="Подчёркивание">
|
||||
<i data-lucide="underline"></i>
|
||||
</button>
|
||||
<button type="button" class="format-btn" @mousedown.prevent="triggerFileInput" title="Прикрепить файл">
|
||||
<i data-lucide="paperclip"></i>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<RichTextEditor
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="$emit('update:modelValue', $event)"
|
||||
:placeholder="replyingTo ? 'Напишите ответ...' : 'Напишите комментарий...'"
|
||||
:show-toolbar="false"
|
||||
ref="editorRef"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- Превью прикреплённых файлов -->
|
||||
<div v-if="files.length > 0" class="attached-files">
|
||||
<div
|
||||
v-for="(file, index) in files"
|
||||
:key="file.name + '-' + index"
|
||||
class="attached-file"
|
||||
>
|
||||
<div class="attached-file-icon">
|
||||
<i v-if="isArchive(file)" data-lucide="archive"></i>
|
||||
<i v-else data-lucide="image"></i>
|
||||
</div>
|
||||
<span class="attached-file-name" :title="file.name">{{ file.name }}</span>
|
||||
<button class="attached-file-remove" @click="removeFile(index)" title="Удалить">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<RichTextEditor
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="$emit('update:modelValue', $event)"
|
||||
:placeholder="replyingTo ? 'Напишите ответ...' : 'Напишите комментарий...'"
|
||||
:show-toolbar="false"
|
||||
ref="editorRef"
|
||||
/>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn-send-comment"
|
||||
@click="$emit('send')"
|
||||
:disabled="!canSend || isSending"
|
||||
>
|
||||
<span v-if="isSending" class="btn-loader"></span>
|
||||
<template v-else>
|
||||
<i data-lucide="send"></i>
|
||||
Отправить
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Скрытый input для файлов -->
|
||||
<!-- Mobile: Кнопка открытия формы -->
|
||||
<template v-else>
|
||||
<button class="btn-open-form" @click="openMobileForm">
|
||||
<i data-lucide="message-square-plus"></i>
|
||||
{{ replyingTo ? 'Написать ответ' : 'Написать комментарий' }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Скрытый input для файлов (общий) -->
|
||||
<input
|
||||
type="file"
|
||||
ref="fileInputRef"
|
||||
@@ -45,42 +86,94 @@
|
||||
style="display: none"
|
||||
>
|
||||
|
||||
<!-- Превью прикреплённых файлов -->
|
||||
<div v-if="files.length > 0" class="attached-files">
|
||||
<div
|
||||
v-for="(file, index) in files"
|
||||
:key="file.name + '-' + index"
|
||||
class="attached-file"
|
||||
>
|
||||
<div class="attached-file-icon">
|
||||
<i v-if="isArchive(file)" data-lucide="archive"></i>
|
||||
<i v-else data-lucide="image"></i>
|
||||
<!-- Mobile: Fullscreen форма -->
|
||||
<Teleport to="body">
|
||||
<Transition name="mobile-form">
|
||||
<div v-if="isMobile && mobileFormOpen" class="mobile-form-overlay">
|
||||
<div class="mobile-form-panel">
|
||||
<div class="mobile-form-header">
|
||||
<button class="btn-close" @click="closeMobileForm">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
<h3 class="panel-title">{{ replyingTo ? 'Ответ' : 'Новый комментарий' }}</h3>
|
||||
<div class="header-spacer"></div>
|
||||
</div>
|
||||
|
||||
<!-- Индикатор "ответ на" -->
|
||||
<div v-if="replyingTo" class="mobile-reply-indicator">
|
||||
<i data-lucide="corner-down-right"></i>
|
||||
<span>Ответ для <strong>{{ replyingTo.author_name }}</strong></span>
|
||||
</div>
|
||||
|
||||
<div class="mobile-form-body">
|
||||
<RichTextEditor
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="$emit('update:modelValue', $event)"
|
||||
:placeholder="replyingTo ? 'Напишите ответ...' : 'Напишите комментарий...'"
|
||||
:show-toolbar="false"
|
||||
ref="mobileEditorRef"
|
||||
class="mobile-editor"
|
||||
/>
|
||||
|
||||
<!-- Превью прикреплённых файлов -->
|
||||
<div v-if="files.length > 0" class="attached-files mobile">
|
||||
<div
|
||||
v-for="(file, index) in files"
|
||||
:key="file.name + '-' + index"
|
||||
class="attached-file"
|
||||
>
|
||||
<div class="attached-file-icon">
|
||||
<i v-if="isArchive(file)" data-lucide="archive"></i>
|
||||
<i v-else data-lucide="image"></i>
|
||||
</div>
|
||||
<span class="attached-file-name" :title="file.name">{{ file.name }}</span>
|
||||
<button class="attached-file-remove" @click="removeFile(index)" title="Удалить">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-form-footer">
|
||||
<div class="mobile-format-buttons">
|
||||
<button class="btn-format" @click="applyMobileFormat('bold')" title="Жирный">
|
||||
<i data-lucide="bold"></i>
|
||||
</button>
|
||||
<button class="btn-format" @click="applyMobileFormat('italic')" title="Курсив">
|
||||
<i data-lucide="italic"></i>
|
||||
</button>
|
||||
<button class="btn-format" @click="applyMobileFormat('underline')" title="Подчёркивание">
|
||||
<i data-lucide="underline"></i>
|
||||
</button>
|
||||
<button class="btn-format" @click="triggerFileInput" title="Прикрепить файл">
|
||||
<i data-lucide="paperclip"></i>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="btn-send"
|
||||
@click="handleMobileSend"
|
||||
:disabled="!canSend || isSending"
|
||||
>
|
||||
<span v-if="isSending" class="btn-loader"></span>
|
||||
<template v-else>
|
||||
<i data-lucide="send"></i>
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="attached-file-name" :title="file.name">{{ file.name }}</span>
|
||||
<button class="attached-file-remove" @click="removeFile(index)" title="Удалить">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn-send-comment"
|
||||
@click="$emit('send')"
|
||||
:disabled="!canSend || isSending"
|
||||
>
|
||||
<span v-if="isSending" class="btn-loader"></span>
|
||||
<template v-else>
|
||||
<i data-lucide="send"></i>
|
||||
Отправить
|
||||
</template>
|
||||
</button>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUpdated } from 'vue'
|
||||
import { ref, computed, onMounted, onUpdated, watch, nextTick } from 'vue'
|
||||
import FormField from '../ui/FormField.vue'
|
||||
import RichTextEditor from '../ui/RichTextEditor.vue'
|
||||
import { useMobile } from '../../composables/useMobile'
|
||||
|
||||
const { isMobile } = useMobile()
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -97,11 +190,47 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['update:modelValue', 'send', 'cancel-reply'])
|
||||
const emit = defineEmits(['update:modelValue', 'send', 'cancel-reply'])
|
||||
|
||||
const editorRef = ref(null)
|
||||
const mobileEditorRef = ref(null)
|
||||
const fileInputRef = ref(null)
|
||||
const files = ref([])
|
||||
const mobileFormOpen = ref(false)
|
||||
|
||||
// Открытие мобильной формы
|
||||
const openMobileForm = async () => {
|
||||
mobileFormOpen.value = true
|
||||
await nextTick()
|
||||
refreshIcons()
|
||||
}
|
||||
|
||||
// Закрытие мобильной формы
|
||||
const closeMobileForm = () => {
|
||||
mobileFormOpen.value = false
|
||||
emit('cancel-reply')
|
||||
}
|
||||
|
||||
// Отправка из мобильной формы
|
||||
const handleMobileSend = () => {
|
||||
emit('send')
|
||||
// Форма закроется после успешной отправки через watch
|
||||
}
|
||||
|
||||
// Закрытие формы когда isSending становится false после отправки
|
||||
watch(() => props.isSending, (newVal, oldVal) => {
|
||||
if (oldVal === true && newVal === false && mobileFormOpen.value) {
|
||||
// Отправка завершена, закрываем форму
|
||||
closeMobileForm()
|
||||
}
|
||||
})
|
||||
|
||||
// Открытие формы при выборе ответа
|
||||
watch(() => props.replyingTo, (newVal) => {
|
||||
if (newVal && isMobile.value) {
|
||||
openMobileForm()
|
||||
}
|
||||
})
|
||||
|
||||
const allowedExtensions = ['png', 'jpg', 'jpeg', 'zip', 'rar']
|
||||
const archiveExtensions = ['zip', 'rar']
|
||||
@@ -197,6 +326,10 @@ const applyFormat = (command) => {
|
||||
editorRef.value?.applyFormat(command)
|
||||
}
|
||||
|
||||
const applyMobileFormat = (command) => {
|
||||
mobileEditorRef.value?.applyFormat(command)
|
||||
}
|
||||
|
||||
const refreshIcons = () => {
|
||||
if (window.lucide) {
|
||||
window.lucide.createIcons()
|
||||
@@ -221,6 +354,7 @@ defineExpose({
|
||||
.comment-form {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
padding-top: 16px;
|
||||
padding-bottom: calc(var(--safe-area-bottom, 0px));
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
@@ -420,4 +554,215 @@ defineExpose({
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
/* ========== MOBILE: Кнопка открытия формы ========== */
|
||||
.btn-open-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: #000;
|
||||
font-family: inherit;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.btn-open-form i {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.btn-open-form:active {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
/* ========== MOBILE: Fullscreen Form ========== */
|
||||
.mobile-form-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: #18181b;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.mobile-form-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mobile-form-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-form-header .panel-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mobile-form-header .btn-close {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-form-header .btn-close i {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.mobile-form-header .header-spacer {
|
||||
width: 36px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-reply-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
background: rgba(0, 212, 170, 0.08);
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.mobile-reply-indicator i {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.mobile-reply-indicator strong {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.mobile-form-body {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mobile-editor {
|
||||
flex: 1;
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.mobile-editor :deep(.editor-content) {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.attached-files.mobile {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.mobile-form-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
padding-bottom: calc(12px + var(--safe-area-bottom, 0px));
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-format-buttons {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.btn-format {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
color: var(--text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-format i {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.btn-format:active {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-send {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 14px;
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: #000;
|
||||
font-family: inherit;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-send i {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.btn-send:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-send:not(:disabled):active {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
/* Mobile form transition */
|
||||
.mobile-form-enter-active,
|
||||
.mobile-form-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.mobile-form-enter-from,
|
||||
.mobile-form-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -402,6 +402,7 @@ defineExpose({
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0; /* Важно для flex overflow */
|
||||
}
|
||||
|
||||
.comments-list {
|
||||
@@ -413,10 +414,19 @@ defineExpose({
|
||||
padding: 2px; /* Для outline при редактировании */
|
||||
padding-right: 6px;
|
||||
margin-bottom: 16px;
|
||||
min-height: 200px;
|
||||
min-height: 100px;
|
||||
max-height: calc(100vh - 400px);
|
||||
}
|
||||
|
||||
/* Mobile: убираем max-height, используем flex */
|
||||
@media (max-width: 768px) {
|
||||
.comments-list {
|
||||
max-height: none;
|
||||
min-height: 50px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.comments-loading,
|
||||
.comments-empty {
|
||||
display: flex;
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<div class="field-row">
|
||||
<div class="field-row" :class="{ mobile: isMobile }">
|
||||
<FormField label="Срок выполнения">
|
||||
<DatePicker v-model="form.dueDate" />
|
||||
</FormField>
|
||||
@@ -106,6 +106,9 @@ import FileUploader from '../ui/FileUploader.vue'
|
||||
import DatePicker from '../DatePicker.vue'
|
||||
import ConfirmDialog from '../ConfirmDialog.vue'
|
||||
import { getFullUrl } from '../../api'
|
||||
import { useMobile } from '../../composables/useMobile'
|
||||
|
||||
const { isMobile } = useMobile()
|
||||
|
||||
const props = defineProps({
|
||||
card: {
|
||||
@@ -370,6 +373,11 @@ defineExpose({
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.field-row.mobile {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.format-buttons {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
@close="tryClose"
|
||||
>
|
||||
<template #header>
|
||||
<h2>{{ panelTitle }}</h2>
|
||||
<span v-if="!isNew && card?.dateCreate" class="header-date">
|
||||
Создано: {{ formatDate(card.dateCreate) }}
|
||||
</span>
|
||||
<div class="header-title-block">
|
||||
<h2>{{ panelTitle }}</h2>
|
||||
<span v-if="!isNew && card?.dateCreate" class="header-date">
|
||||
Создано: {{ formatDate(card.dateCreate) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Вкладки (только для существующих задач) -->
|
||||
<TabsPanel
|
||||
@@ -43,7 +45,8 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<!-- Footer: скрываем на вкладке комментариев -->
|
||||
<template #footer v-if="activeTab !== 'comments'">
|
||||
<div class="footer-left">
|
||||
<IconButton
|
||||
v-if="!isNew"
|
||||
@@ -146,6 +149,9 @@ import ConfirmDialog from '../ConfirmDialog.vue'
|
||||
import TaskEditTab from './TaskEditTab.vue'
|
||||
import TaskCommentsTab from './TaskCommentsTab.vue'
|
||||
import { taskImageApi, commentImageApi, getFullUrl } from '../../api'
|
||||
import { useMobile } from '../../composables/useMobile'
|
||||
|
||||
const { isMobile } = useMobile()
|
||||
|
||||
const props = defineProps({
|
||||
show: Boolean,
|
||||
@@ -402,7 +408,7 @@ watch(() => props.show, async (newVal) => {
|
||||
|
||||
await nextTick()
|
||||
editTabRef.value?.saveInitialForm()
|
||||
editTabRef.value?.focusTitle()
|
||||
// Не фокусируем поле автоматически, чтобы не открывалась клавиатура
|
||||
refreshIcons()
|
||||
}
|
||||
})
|
||||
@@ -412,13 +418,22 @@ onUpdated(refreshIcons)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header-title-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.header-title-block h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-date {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.header-tabs {
|
||||
margin-left: auto;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user