Keep desktop modals open on outside click and allow larger group member lists.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-04 12:12:05 +03:30
co-authored by Cursor
parent 8b37525f2f
commit 39e2eb24a6
3 changed files with 148 additions and 30 deletions
+27 -7
View File
@@ -2,12 +2,23 @@
import { onUnmounted, watch } from 'vue';
import UiIcon from '@/components/UiIcon.vue';
const props = defineProps<{
open: boolean;
title: string;
/** برای مودال روی مودال (مثل انتخاب مخاطب داخل فرم) */
nest?: boolean;
}>();
const props = withDefaults(
defineProps<{
open: boolean;
title: string;
/** برای مودال روی مودال (مثل انتخاب مخاطب داخل فرم) */
nest?: boolean;
/**
* بستن با کلیک روی پس‌زمینه.
* پیش‌فرض: فقط موبایل (تمام‌صفحه). در دسکتاپ عمداً بسته نمی‌شود.
*/
closeOnBackdrop?: boolean | 'mobile';
}>(),
{
nest: false,
closeOnBackdrop: 'mobile',
},
);
const emit = defineEmits<{
close: [];
@@ -24,8 +35,17 @@ onUnmounted(() => {
document.body.style.overflow = '';
});
function isMobileViewport() {
return typeof window !== 'undefined' && window.matchMedia('(max-width: 900px)').matches;
}
function onBackdrop(event: MouseEvent) {
if (event.target === event.currentTarget) emit('close');
if (event.target !== event.currentTarget) return;
const mode = props.closeOnBackdrop;
if (mode === false) return;
if (mode === true || (mode === 'mobile' && isMobileViewport())) {
emit('close');
}
}
</script>
+81 -15
View File
@@ -1575,8 +1575,17 @@ async function startGroup() {
groupMemberIds.value = [];
await openRoom(data);
} catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 403) {
error.value = 'اجازه ساخت گروه را ندارید.';
if (axios.isAxiosError(e)) {
const msg = (e.response?.data as { message?: string | string[] })?.message;
if (e.response?.status === 403) {
error.value = 'اجازه ساخت گروه را ندارید.';
} else if (Array.isArray(msg)) {
error.value = msg.join('، ');
} else if (typeof msg === 'string' && msg.trim()) {
error.value = msg;
} else {
error.value = 'ساخت گروه ناموفق بود.';
}
} else {
error.value = 'ساخت گروه ناموفق بود.';
}
@@ -1620,6 +1629,13 @@ function toggleChannelMember(id: string) {
}
}
/** در دسکتاپ با کلیک بیرون مودال بسته نشود (انتخاب اعضا قطع نشود) */
function closeModalOnBackdrop(close: () => void) {
if (typeof window !== 'undefined' && window.matchMedia('(max-width: 900px)').matches) {
close();
}
}
function openNewDm() {
peerPickerQuery.value = '';
dmPeerId.value = '';
@@ -2478,7 +2494,12 @@ onUnmounted(() => {
</section>
</div>
<dialog v-if="showNewDm" class="modal" open @click.self="showNewDm = false">
<dialog
v-if="showNewDm"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showNewDm = false))"
>
<div class="modal-card wide">
<h3>گفتگوی جدید</h3>
<p class="hint">همکار موردنظر را انتخاب کنید</p>
@@ -2518,8 +2539,13 @@ onUnmounted(() => {
</div>
</dialog>
<dialog v-if="showNewGroup" class="modal" open @click.self="showNewGroup = false">
<div class="modal-card wide">
<dialog
v-if="showNewGroup"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showNewGroup = false))"
>
<div class="modal-card wide member-picker-modal">
<h3>گروه جدید</h3>
<input v-model="groupName" type="text" placeholder="نام گروه" />
<div class="picker-head">
@@ -2585,8 +2611,13 @@ onUnmounted(() => {
</div>
</dialog>
<dialog v-if="showNewChannel" class="modal" open @click.self="showNewChannel = false">
<div class="modal-card wide">
<dialog
v-if="showNewChannel"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showNewChannel = false))"
>
<div class="modal-card wide member-picker-modal">
<h3>کانال جدید</h3>
<p class="hint channel-hint">
کانال یک‌طرفه است؛ فقط شما و مدیران سازمان می‌توانند پیام بفرستند.
@@ -2659,7 +2690,7 @@ onUnmounted(() => {
v-if="showActivityFromMsg"
class="modal"
open
@click.self="closeActivityFromMsg"
@click.self="closeModalOnBackdrop(closeActivityFromMsg)"
>
<div class="modal-card activity-sheet">
<template v-if="!activityCreatedId">
@@ -2706,7 +2737,12 @@ onUnmounted(() => {
</div>
</dialog>
<dialog v-if="showCrmLink" class="modal" open @click.self="showCrmLink = false">
<dialog
v-if="showCrmLink"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showCrmLink = false))"
>
<div class="modal-card wide">
<h3>لینک CRM</h3>
<p class="hint">شخص، معامله یا تیکت را انتخاب کنید تا در پیام درج شود.</p>
@@ -2765,7 +2801,12 @@ onUnmounted(() => {
</div>
</dialog>
<dialog v-if="showAiAssist" class="modal" open @click.self="showAiAssist = false">
<dialog
v-if="showAiAssist"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showAiAssist = false))"
>
<div class="modal-card wide ai-assist-sheet">
<h3>{{ brand.askLabelFa }}</h3>
<p class="hint">با توجه به CRM و چند پیام اخیر این گفتگو پاسخ می‌دهد؛ می‌توانید پاسخ را در پیش‌نویس درج کنید.</p>
@@ -2781,8 +2822,13 @@ onUnmounted(() => {
</div>
</dialog>
<dialog v-if="showMembers" class="modal" open @click.self="showMembers = false">
<div class="modal-card wide members-sheet">
<dialog
v-if="showMembers"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showMembers = false))"
>
<div class="modal-card wide members-sheet member-picker-modal">
<h3>اعضای {{ activeRoom ? roomTitle(activeRoom) : 'گفتگو' }}</h3>
<p v-if="activeRoom?.kind === 'channel'" class="hint channel-hint">
ادمین‌ها می‌توانند در کانال پیام بفرستند؛ بقیه فقط مشاهده می‌کنند.
@@ -2839,7 +2885,12 @@ onUnmounted(() => {
</div>
</dialog>
<dialog v-if="showForward" class="modal" open @click.self="showForward = false">
<dialog
v-if="showForward"
class="modal"
open
@click.self="closeModalOnBackdrop(() => (showForward = false))"
>
<div class="modal-card">
<h3>ارسال برای دیگری</h3>
<p class="hint">{{ toFaDigits(selectedCount) }} پیام انتخاب شده</p>
@@ -4212,9 +4263,22 @@ onUnmounted(() => {
}
.modal-card.wide {
width: min(440px, 100%);
max-height: min(88vh, 640px);
max-height: min(88vh, 720px);
overflow: auto;
}
.modal-card.member-picker-modal {
display: flex;
flex-direction: column;
gap: 0.75rem;
overflow: hidden;
min-height: min(70vh, 560px);
}
.modal-card.member-picker-modal > .peer-list,
.modal-card.member-picker-modal > .members-list {
flex: 1 1 auto;
min-height: 220px;
max-height: none;
}
.modal-card select,
.modal-card input[type='text'],
.modal-card input[type='search'],
@@ -4302,10 +4366,12 @@ onUnmounted(() => {
display: flex;
flex-direction: column;
gap: 0.3rem;
max-height: 240px;
max-height: min(48vh, 360px);
overflow: auto;
overscroll-behavior: contain;
padding: 0.15rem;
margin: 0 -0.15rem;
-webkit-overflow-scrolling: touch;
}
.peer-row {
display: grid;