نافذة معاينة الموضوع

تثبيت مكون السمة هذا

نافذة معاينة الموضوع – افتح وتفاعل مع المواضيع دون مغادرة قائمة المواضيع

لقد قمت بإنشاء مكون سمة جديد لـ Discourse يُدعى نافذة معاينة الموضوع.

الفكرة بسيطة إلى حد ما:

افتح موضوعاً مباشرةً من قائمة المواضيع في نافذة Discourse الأصلية، اقرأ وتفاعل مع الموضوع، ثم واصل تصفح القائمة دون الحاجة إلى مغادرتها.

بدأت الفكرة من Facebook-style Topic Modal - Is it better? ، لكنها انتهت بضرورة التكامل إلى حد كبير مع أنظمة Discourse الخاصة بالمواضيع، وتدفق المنشورات، والمحرر، والنوافذ المنبثقة، والإشارات المرجعية، والتوجيه، والوجود، وتتبع القراءة، والتحميل المسبق.


لماذا؟

التدفق العادي في Discourse هو:

  1. أنت تصفح قائمة مواضيع.
  2. تنقر على موضوع.
  3. ينقلك Discourse إلى /t/....
  4. تقرأ/ترد/تتفاعل مع الموضوع.
  5. تعود إلى قائمة المواضيع.

بالنسبة للعديد من سير العمل، هذا مقبول تماماً.

ومع ذلك، عند تصفح قائمة مواضيع مزدحمة، أحياناً أريد فقط فحص موضوع بسرعة، وقراءة بعض المنشورات، والتحقق من أحدث الردود، والتفاعل مع شيء ما، أو الإجابة على سؤال سريع.

لهذا الغرض، يبدو مغادرة قائمة المواضيع مكلفاً بشكل غير ضروري.

لذلك كان الهدف من هذا المكون هو جعل قائمة المواضيع تتصرف أكثر مثل صندوق الوارد:

قائمة المواضيع → معاينة → تفاعل → إغلاق → الاستمرار من حيث توقفت بالضبط.


ماذا يفعل

المعاينة ليست مجرد مقتطف ثابت.

يقوم بعرض مكونات المنشورات الفعلية لـ Discourse داخل DModal الأصلي.

هذا يعني أن المستخدمين يمكنهم:

  • قراءة المنشورات
  • التمرير عبر الموضوع
  • تحميل المنشورات السابقة
  • تحميل المزيد من المنشورات أدناه
  • التفاعل مع المنشورات
  • وضع إشارات مرجعية للمنشورات
  • اقتباس النص
  • الرد على الموضوع
  • الرد على منشورات فردية
  • تعديل المنشورات عند الإذن
  • حذف/استعادة المنشورات عند الإذن
  • الإبلاغ عن المنشورات
  • عرض تاريخ المنشور
  • تنفيذ إجراءات المنشور العادية المختلفة
  • رؤية وجود الموضوع
  • متابعة الروابط إلى منشورات أخرى داخل نفس الموضوع
  • القفز مباشرةً إلى المنشور ذي الصلة
  • فتح الموضوع الكامل عند الحاجة

النية هي أن تشعر المعاينة بقرب كبير من فتح الموضوع فعلياً.


وضعان للتفعيل

هناك طريقتان لفتح المعاينة.

1. صف قائمة الموضوع بالكامل

هذا هو الوضع الافتراضي.

يصبح صف قائمة الموضوع بالكامل قابلاً للنقر، بينما يتم استبعاد العناصر التفاعلية الشائعة مثل:

  • بطاقات المستخدمين
  • المشاركين
  • روابط الفئات
  • الوسوم
  • روابط حالة الموضوع
  • التحديد بالجملة

من تفعيل النافذة المنبثقة.

هذا يجعل التجربة سريعة جداً عند تصفح قائمة المواضيع.

2. زر التوسيع الصريح

بدلاً من ذلك، يمكن للمكون عرض أيقونة توسيع صغيرة عبر منفذ إضافة Discourse. يمكن للسمات المخصصة ببساطة إنشاء <PluginOutlet /> جديد لإظهار الزر.

في هذا الوضع، تظل سلوك قائمة الموضوع العادي دون تغيير تماماً.

ينقر المستخدم على أيقونة التوسيع لفتح المعاينة، بينما يؤدي النقر على عنوان الموضوع إلى تنفيذ التوجيه العادي لـ Discourse.

هذا مفيد إذا أرادت الموقع الحفاظ على نموذج التفاعل القياسي لقائمة المواضيع.

الإعداد هو:

trigger_style:
  row

أو:

trigger_style:
  button

عند استخدام وضع الزر، يمكن تكوين المنفذ أيضاً.


تبدأ المعاينة من موضع المستخدم غير المقروء

إحدى التفاصيل المهمة هي أن النافذة المنبثقة لا تقوم ببساطة بتحميل المنشور الأول.

عندما يكون الموضوع قد تم قراءته جزئياً بالفعل، تحسب المعاينة:

last_read_post_number + 1

وتفتح حول ذلك المنشور.

لذا إذا كان الموضوع يحتوي على 200 منشور وقام المستخدم بقراءة المنشور رقم 165، تبدأ المعاينة حول رقم 166.

هذا يجعل المعاينة أكثر فائدة للتصفح في العالم الحقيقي.

هذا يعني أيضاً أن المكون يجب أن يتعامل مع كلا جانبي تدفق المنشورات:

  • تحميل المنشورات السابقة عند الضرورة
  • تحميل المنشورات الأحدث أدناه

يتم عرض زر المنشورات السابقة عندما تكون هناك منشورات فوق النطاق المحمل حالياً، بينما يقوم IntersectionObserver sentinel تلقائياً بتحميل المزيد من المنشورات عندما يصل المستخدم إلى الأسفل.


التحميل المسبق

إحدى أكبر أجزاء المكون هي نظام التحميل المسبق الخاص به.

المشكلة مع نافذة منبثقة مثل هذه هي أن المستخدم يتوقع أن تشعر لحظية.

إذا بدأنا تحميل الموضوع فقط بعد أن ينقر المستخدم، قد تقضي النافذة المنبثقة وقتاً ملحوظاً في الانتظار للشبكة.

بدلاً من ذلك، يمكن للمكون تحميل المواضيع بشكل استباقي أثناء تصفح المستخدم للقائمة.

عندما يقترب صف الموضوع من إطار العرض، يمكن لـ IntersectionObserver جدولة تحميل مسبق.

هناك عدة ضوابط لمنع هذا من التحول إلى حركة خلفية غير خاضعة للرقابة.

التخزين المؤقت (Debouncing)

لا يحفز الموضوع طلباً فوراً لمجرد ظهوره لفترة وجيزة في إطار العرض.

ينتظر المكون فترة التخزين المؤقت المكونة.

الافتراضي:

400 ms

هذا مفيد بشكل خاص عند التمرير السريع عبر قائمة مواضيع طويلة.

هامش الجذر (Root margin)

يمكن أن يبدأ التحميل المسبق قليلاً قبل دخول الموضوع فعلياً إلى إطار العرض.

الافتراضي:

50 px

هذا يمنح الطلب بداية صغيرة.

حد الطلبات المتزامنة

يتم تحديد عدد عمليات التحميل المسبق المتزامنة.

الافتراضي:

2

يسمح الإعداد بين 1 و 6 عمليات تحميل مسبق متزامنة.

ميزانية لكل دقيقة

هناك أيضاً آلية حماية ثانية:

max_prefetches_per_minute

الافتراضي هو:

15

لذا حتى إذا واصل المستخدم التمرير عبر مئات المواضيع، لن يولد المكون طلبات تنبؤية بشكل مستمر.

0 يعطل الحد.

يمكن تعطيل التحميل المسبق تماماً

إذا لم يرد الموقع أي حركة شبكة تنبؤية:

enable_prefetch = false

يستمر المكون في العمل بشكل طبيعي. ببساطة يتم تحميل المواضيع عند فتح المعاينة.


بيانات التحميل المسبق تبقى منفصلة عن التوجيه العادي للموضوع

هناك تفصيل تنفيذي مهم هنا.

لا يتم كتابة استجابة التحميل المسبق فوراً إلى مفتاح التحميل المسبق العادي topic_<id> الخاص بـ Discourse.

بدلاً من ذلك، يستخدم المكون مساحته الاسمية الخاصة:

topic-preview-modal:prefetch:<topicId>

فقط عندما يفتح المستخدم المعاينة فعلياً، يتم ترقية وعد التحميل المسبق إلى مفتاح التحميل المسبق الأساسي للموضوع.

هذا متعمد.

قد تقوم المعاينة بتحميل موضوع بدءاً من last_read_post_number + 1، ولا أريد أن تتسرب استجابة المعاينة المحددة هذه إلى توجيه مسار موضوع عادي.

لذا فإن دورة الحياة هي في الأساس:

dخول الموضوع إلى إطار العرض
        ↓
التحميل المسبق
        ↓
تخزين التحميل المسبق الخاص
        ↓
المستخدم يفتح المعاينة
        ↓
ترقية التحميل المسبق
        ↓
Topic.find()/PostStream يستخدم نفس الوعد

هذا يعني أيضاً أن النافذة المنبثقة لا تضطر إلى الانتظار حتى انتهاء طلب التحميل المسبق قبل الفتح.

يمكن فتح النافذة المنبثقة فوراً بهيكلها العظمي بينما يستمر نفس الوعد في الحل.


دعم الجوال

كان هذا في الواقع أحد الأسباب التي جعلني أقضي وقتاً أكبر بكثير على التنفيذ.

عملت الفكرة الأولية بشكل معقول على سطح المكتب، لكن الجوال كشف عدة مشاكل حول:

  • التفاعل باللمس
  • التمرير في النافذة المنبثقة
  • التركيز (Focus)
  • القوائم المتداخلة
  • المحرر
  • رؤية المنشور
  • تحميل الصور
  • الأداء

لذلك يتجنب التنفيذ النهائي التعامل مع النافذة المنبثقة كمنتدى مصغر منفصل تماماً.

بدلاً من ذلك، يعيد استخدام أكبر قدر ممكن من البنية التحتية الحالية لـ Discourse.


مكونات منشورات Discourse الحقيقية

لا تعيد النافذة المنبثقة إنشاء المنشورات باستخدام قالب مخصص مبسط.

تعرض مكونات Discourse الفعلية:

Post
PostSmallAction

هذا مهم لأنه وإلا فإن المعاينة ستصبح بسرعة تنفيذاً ثانياً لواجهة المستخدم للمنشور.

يمرر المكون الإجراءات ذات الصلة إلى مكونات المنشور العادية، بما في ذلك أشياء مثل:

  • الرد
  • التعديل
  • الحذف
  • الاستعادة
  • الإبلاغ
  • التاريخ
  • الإشارة المرجعية
  • الويكي
  • القفل/إلغاء القفل
  • نوع المنشور
  • تغييرات الملكية
  • الأوسمة
  • المنشورات المخفية
  • الاقتباس
  • إلخ.

النتيجة هي أن المعاينة يمكن أن تتصرف أكثر مثل موضوع عادي بدلاً من مكون “معاينة” تقليدي.


الردود والمحرر

المحرر هو أحد الأجزاء الأكثر تعقيداً.

يمكن للمعاينة فتح محرر Discourse العادي لـ:

الرد على الموضوع

يتم فتح محرر الموضوع مع نموذج الموضوع ومعلومات المسودة الصحيحة.

الرد على منشور محدد

يتم تمرير المنشور إلى المحرر بحيث يتصرف الرد مثل رد منشور عادي.

اقتباس نص محدد

يتكامل المكون أيضاً مع PostTextSelection.

هذا يعني أن المستخدمين يمكنهم تحديد النص داخل المعاينة واستخدام تدفق الاقتباس/الرد العادي لـ Discourse.


النوافذ المنبثقة المتداخلة

كان الجزء الصعب الآخر هو نظام النوافذ المنبثقة في Discourse.

يمكن للمنشورات فتح نوافذ منبثقة وحوارات أخرى:

  • الإبلاغ
  • التاريخ
  • حوارات متعلقة بالأوسمة
  • تغييرات الملكية
  • تأكيدات الحذف
  • إلخ.

إذا سمح لها بالتفاعل مع خدمة النافذة المنبثقة العالمية بشكل عادي، يمكن لفتح واحدة منها إغلاق معاينة الموضوع بأكملها.

لتجنب ذلك، ينشئ المكون آلية نافذة فرعية محلية.

مفاهيمياً:

نافذة معاينة الموضوع
        │
        ├── نافذة الإبلاغ
        ├── نافذة التاريخ
        ├── تأكيد الحذف
        ├── نافذة الأوسمة
        └── نوافذ أخرى متعلقة بالمنشور

تبقى المعاينة مثبتة في الأسفل.

يقوم المكون بتصحيح طرق خدمة النافذة المنبثقة ذات الصلة مؤقتاً أثناء نشاطه واستعادتها عند تدميره.


التوجيه داخل النافذة المنبثقة

تفصيل مهم آخر هو الروابط إلى المنشورات داخل نفس الموضوع.

على سبيل المثال، إذا احتوى منشور على رابط إلى:

/t/my-topic/123

لا تحتاج المعاينة إلى الإغلاق والمغادرة.

بدلاً من ذلك، يتدخل المكون في التنقل داخل نفس الموضوع ويقفز إلى المنشور المطلوب داخل النافذة المنبثقة.

ينطبق الشيء نفسه على الروابط المستهدفة للموضوع بدون رقم منشور محدد.

هذا يبقي المستخدم داخل المعاينة.

إذا أشار الرابط إلى موضوع مختلف حقاً، يقوم المكون أولاً باستعادة تصحيحات الخدمة المؤقتة وإغلاق نفسه قبل السماح بانتقال المسار العادي لـ Discourse.

هذا التنظيف مهم لأنه وإلا قد تبقى اشتراكات المعاينة ومتتبع الوقت نشطة أثناء تهيئة مسار الموضوع الحقيقي.


تتبع القراءة وتتبع الوقت

أردت أيضاً أن تتصرف المعاينة بشكل صحيح من منظور Discourse.

لا ينبغي أن يعني فتح معاينة أن تتبع القراءة يتم تخطيه تماماً.

لذلك يتعامل المكون مع:

  • تتبع زيارة الموضوع
  • تتبع المنشور المرئي
  • توقيت الموضوع
  • تحديثات آخر منشور مقروء

يستخدم متتبع الوقت IntersectionObserver لتحديد المنشورات المرئية فعلياً.

كل 5 ثوانٍ، يتم تفريغ توقيت المنشور المرئي إلى:

/topics/timings

عند إغلاق النافذة المنبثقة، يتم تنفيذ تفريغ نهائي واحد بحيث لا تضيع الثوانى الأخيرة.

يحدد التنفيذ أيضاً فترة توقيت واحدة بحد أقصى 60 ثانية.


الحفاظ على مزامنة حالة غير المقروء في قائمة المواضيع

كان هناك مشكلة دقيقة أخرى هنا.

تحديث حالة تتبع الموضوع في Discourse وحدها لا تكفي لتحديث شارة غير المقروء المعروضة مباشرةً على صف قائمة المواضيع.

لذلك يقوم المكون بتحديث كائن الموضوع الفعلي المرتبط بالصف بعد تفريغ معلومات التوقيت.

يقوم بتحديث قيم مثل:

last_read_post_number
unread_posts
unread
new_posts

عند الاقتضاء.

هذا يعني أنه بعد قراءة موضوع داخل النافذة المنبثقة، يمكن لقائمة المواضيع عكس حالة القراءة الجديدة فوراً بدلاً من الحاجة إلى تحديث كامل للصفحة.


رؤية المنشور

تستخدم المعاينة IntersectionObserver مشترك لتحديد متى تصبح المنشورات الفردية مرئية.

هناك أيضاً فحص رؤية متزامن عند إرفاق المراقب.

هذا يتعامل مع حالة حافة حيث يكون المنشور مرئياً بالفعل عند تثبيته، لكن استدعاء IntersectionObserver الأول غير المتزامن لم يتم تشغيله بعد.

هذا ذو صلة خاصة بالمواضيع القصيرة جداً حيث قد يكون الموضوع بأكمله مرئياً بالفعل عند فتح النافذة المنبثقة.


اعتبارات الأداء

كان الهدف الرئيسي هو تجنب تحويل النافذة المنبثقة إلى صفحة موضوع مصغر ثقيل الأداء.

يتم القيام بعدة أشياء لهذا الغرض تحديداً.

العرض التدريجي

لا يقوم التحميل الأولي بعرض كل منشور على الفور.

يقوم المكون أولاً بعرض ما يكفي من المنشورات للوصول إلى الموضع المستهدف.

ثم يتم عرض المنشورات المتبقية بشكل تدريجي باستخدام:

requestIdleCallback

عند توفره، مع بديل إلى setTimeout.

هذا مفيد بشكل خاص عند فتح موضوع طويل حول منشور بعيد جداً في التدفق.

احتواء CSS

تستخدم المنشورات:

contain: layout;
content-visibility: auto;
contain-intrinsic-size: 1px 180px;

هذا يسمح للمتصفح بتجنب القيام بعمل عرض غير ضروري للمنشورات غير المرئية حالياً.

الصور الكسولة (Lazy images)

تُمنح الصور التي لم تحدد بالفعل وضع تحميل تلقائياً:

loading="lazy"
decoding="async"

هذا يمنع موضوعاً طويلاً يحتوي على العديد من الصور من تحميل كل شيء على الفور.


حالة التحميل

لا تعرض النافذة المنبثقة مساحة بيضاء/فارغة فقط أثناء تقديم الطلب.

لديها واجهة مستخدم هيكلية عظمية مع:

  • عناصر نائبة للصورة الرمزية
  • عناصر نائبة لاسم المستخدم/الاسم
  • عناصر نائبة لجسم المنشور
  • تأثير لمعان (shimmer)

يحترم اللمعان:

prefers-reduced-motion

لذلك يتم تعطيل التأثير للمستخدمين الذين طلبوا تقليل الحركة.


الحفاظ على استقرار موضع التمرير

هناك عدة أماكن يحتاج فيها المكون إلى التلاعب بموضع التمرير يدوياً.

على سبيل المثال، عند تحميل المنشورات السابقة، يزيد المحتوى المُدخل حديثاً من ارتفاع التمرير.

سيؤدي إدراج المنشورات ببساطة إلى جعل الموضع الحالي للمستخدم يقفز.

لذلك يسجل المكون ارتفاع التمرير السابق ويعوض الفرق بعد إدراج المنشورات.

هذا يبقي المحتوى المرئي حالياً في نفس المكان تقريباً.

ينطبق الشيء نفسه عند القفز إلى منشور معين.

يقوم المكون بخطوة تموضع بعد العرض ويتحقق من الموضع مرة أخرى في الإطارات اللاحقة لحساب المحتوى الذي قد لا يزال يستقر.


وجود الموضوع

عند توفر بيانات الموضوع ذات الصلة، يمكن للمعاينة أيضاً عرض معلومات وجود الموضوع لـ Discourse في أسفل النافذة المنبثقة.

لذا يمكن للمستخدمين رؤية من يشاهد الموضوع حالياً دون الحاجة إلى مغادرة المعاينة.


التفاعل مع قوائم الجوال والتركيز

قدم الجوال فئة أخرى من المشاكل.

تستخدم بعض عناصر واجهة المستخدم في Discourse خدمات نافذة/قائمة مشتركة، وهذه الخدمات لا تعرف بالضرورة أن معاينة الموضوع تعمل حالياً كسياق تصفح متداخل.

لذلك يحتوي المكون على معالجة إضافية حول:

  • modal.close()
  • قوائم Float Kit
  • استعادة التركيز
  • المحرر
  • أزرار لوحة المفاتيح للعارض الضوئي (lightbox)
  • أقفال تمرير الجسم (body scroll locks)

على سبيل المثال، إذا حاولت القائمة داخلياً استدعاء طريقة إغلاق النافذة المنبثقة العالمية، فلا ينبغي أن تغلق معاينة الموضوع بأكملها بالخطأ.

وبالمثل، عندما يكون المحرر مفتوحاً، يجب أن يبقى التركيز داخل المحرر بدلاً من سحبه مرة أخرى إلى سياق التركيز في المعاينة.


التكوين

يقوم المكون حالياً بتعريض الإعدادات التالية:

الإعداد الافتراضي الوصف
trigger_style row جعل الصف بالكامل قابلاً للنقر أو استخدام زر صريح
plugin_outlet topic-list-after-title المنفذ المستخدم بواسطة زر التفعيل
enable_prefetch true تمكين/تعطيل التحميل المسبق للمواضيع في الخلفية
max_concurrent_prefetches 2 الحد الأقصى لطلبات التحميل المسبق المتزامنة
prefetch_debounce_ms 400 التأخير قبل بدء التحميل المسبق
prefetch_root_margin_px 50 بدء التحميل المسبق قبل دخول الصف إلى إطار العرض بهذا العدد من البكسلات
max_prefetches_per_minute 15 الحد الأقصى لطلبات التخمين لكل دقيقة

تكون عناصر التحكم في التحميل المسبق قابلة للتكوين عمداً لأن المجتمعات المختلفة يمكن أن يكون لها أنماط حركة مرور وخصائص استضافة/شبكة مختلفة جداً.


أحد الأهداف التصميمية الرئيسية: عدم كسر Discourse العادي

حاولت إبقاء المكون قريباً قدر الإمكان من البنية الحالية لـ Discourse.

لا يطبق محرك عرض منشورات خاص به، أو محرر خاص به، أو نموذج موضوع خاص به، أو تدفق منشورات منفصل تماماً خاص به.

بدلاً من ذلك، يبني سياق تصفح مؤقت حول مكونات وخدمات Discourse الحالية.

هذا أيضاً هو السبب في أن بعض أجزاء التنفيذ أكثر تعقيداً مما قد يبدو في البداية.

كان التحدي الأكثر إثارة:

**هل يمكن لموضوع أن يتصرف تقريباً مثل موضوع Discourse عادي بينما يتم عرضه فعلياً داخل سياق واجهة مستخدم آخر؟

تطلب ذلك التعامل مع الحدود بين الخدمات العالمية لـ Discourse والمعاينة المحلية.

5 إعجابات

لمعلوماتك:

يوجد به مشاكل في الرياضيات. لكن هذا يمكن أن يكون حالة حدية أخرى، رغم ذلك.

أسطورة مطلقة :slight_smile: الآن لا بد من معرفة كيفية جعل هذا يعمل مع إعدادي :slight_smile: @awesomerobot ما الذي يعتمد عليه سماتك لنقرة السطر بالكامل

api.renderInOutlet("topic-list-before-link", TopicListItemClick);
إعجاب واحد (1)

بالنسبة لأي شخص يستخدم سمة Reddit-ish، إليك حل نجحت في تطبيقه.

توافق سمة Reddit-ish

ملاحظة سريعة لأي شخص يستخدم سمة Reddit-ish: زر النافذة المنبثقة (modal) نفسه يعمل، لكن مُفعّل الصف (row trigger) الافتراضي لا يعمل.

تكمن المشكلة في أن سمة Reddit-ish تحل محل سلوك صف قائمة المواضيع القياسي، وتتعامل مع النقرات على بطاقة الموضوع بأكملها. وبسبب ذلك، لا يعمل التعامل العادي مع نقرات الصف في النافذة المنبثقة كما هو مقصود.

تغيير إعداد “نافذة معاينة الموضوع” (Topic Preview Modal) إلى:

نوع المُفعّل: زر
منفذ الإضافة: topic-list-after-title

يعمل بشكل صحيح، لأن سمة Reddit-ish تتضمن بالفعل منفذ topic-list-after-title.

للحفاظ على سلوك النقر على البطاقة بأكملها، تركت نافذة معاينة الموضوع في وضع الزر، وقمت بتغيير إجراء openTopic() الموجود في سمة Reddit-ish بحيث يُفعّل زر المعاينة العامل.

إجراء Reddit-ish الأصلي هو:

@action
openTopic(event) {
  if (
    (event.target.nodeName === "A" && !event.target.closest(".raw-link")) ||
    event.target.closest(".badge-wrapper")
  ) {
    return;
  }

  const { navigateToTopic, topic } = this.args.outletArgs;

  if (wantsNewWindow(event)) {
    window.open(topic.lastUnreadUrl, "_blank");
  } else {
    navigateToTopic(topic, topic.lastUnreadUrl);
  }
}

قمت بتغييره إلى:

@action
openTopic(event) {
  if (
    (event.target.nodeName === "A" && !event.target.closest(".raw-link")) ||
    event.target.closest(".badge-wrapper") ||
    event.target.closest(".topic-preview-modal__trigger-wrapper")
  ) {
    return;
  }

  const { navigateToTopic, topic } = this.args.outletArgs;

  if (wantsNewWindow(event)) {
    window.open(topic.lastUnreadUrl, "_blank");
    return;
  }

  const previewButton = event.currentTarget.querySelector(
    ".topic-preview-modal__trigger-wrapper--button"
  );

  if (previewButton) {
    event.preventDefault();
    event.stopPropagation();
    previewButton.click();
    return;
  }

  navigateToTopic(topic, topic.lastUnreadUrl);
}

يتم عرض مُفعّل زر النافذة المنبثقة كالتالي:

<div class="topic-preview-modal__trigger-wrapper">
  <span
    role="button"
    class="topic-preview-modal__trigger-wrapper--button"
  >

لذلك، لا يعيد هذا أي منطق للنافذة المنبثقة. إنه ببساطة يجعل نقر بطاقة Reddit-ish يُفعّل زر المعاينة العامل الموجود مسبقًا.

النتيجة هي:

  • يفتح نقر بطاقة الموضوع نافذة المعاينة المنبثقة.

  • يفتح نقر عنوان الموضوع نافذة المعاينة المنبثقة.

  • لا يزال زر المعاينة يعمل.

  • لا يزال النقر مع Cmd/Ctrl يفتح الموضوع العادي في علامة تبويب جديدة.

  • تستمر الروابط العادية مثل الفئة وغيرها في العمل بشكل طبيعي.

  • إذا لم يكن زر المعاينة موجودًا، تعود سمة Reddit-ish إلى التنقل العادي في المواضيع.

لذلك، تعمل النافذة المنبثقة الأساسية بشكل جيد مع سمة Reddit-ish؛ عدم التوافق محدد مع مُفعّل الصف الافتراضي.

كما قمت بإخفاء الزر باستخدام

.topic-preview-modal__trigger-wrapper {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}
إعجاب واحد (1)