پرتوپرتو

PostMedia (پست‌مدیا)

رندرر چندحالته‌ی رسانه برای پست‌ها — تک‌تصویری، گرید، کاروسل، ویدیو، صوت با حالت‌های loading / failed / unavailable / expired / source-removed و بلر NSFW.

معرفی

<PostMedia> تنها رنده‌رر canonical برای محتوای رسانه‌ای پست‌هاست. روی body.type در PostBodyData دیسپچ می‌کند و همه‌ی حالت‌های دانلود/کیفیت/حساسیت را با یک API مدیریت می‌کند.

چه زمانی استفاده کنیم:

  • هرجا که در داخل <PostCard> یا <PostDetailsDrawer> رسانه‌ی پست را رندر می‌کنید.
  • وقتی بدنه‌ی پست (PostBodyData) چند شکل دارد (تک‌تصویر، گرید، کاروسل، ویدیو، صوت، استوری) و می‌خواهید یک dispatcher واحد همه را پوشش دهد.
  • وقتی وضعیت دانلود (loading / failed / unavailable / expired) یا محتوای حساس (nsfw / graphic / spoiler) باید با قرارداد یکسان placeholder و blur مدیریت شود.

چه زمانی استفاده نکنیم:

  • برای تصاویر تکی غیر پست — به جای آن از <SafeImage> یا <AspectRatio> استفاده کنید.
  • برای بدنه‌ی صرفاً متنی — <PostMedia> با body.type === 'text' هیچ‌چیزی رندر نمی‌کند؛ متن را <PostBody> والد رندر می‌کند.
  • برای گالری تصاویر مستقل از ساختار پست — از Carousel پایه استفاده کنید.

زمین بازی

زمین بازی
نمونه تصویر
تنظیمات
ظاهر
کد این نمونه به‌صورت خودکار قابل تولید نیست — برای کد آماده‌ی copy/paste به بخش «استفاده» در بالای صفحه مراجعه کنید.

استفاده

import { PostMedia } from '@partodata/ui'
;<PostMedia body={post.body} context="comfortable" onRetry={(item) => refetch(item.url)} />

حالت‌ها

  • media-single — تک‌تصویر؛ در فید کراپ (cover) و در نمای جزئیات letterbox با backdrop بلرشده
  • media-grid (۲–۴ تصویر) — لِی‌اوت Instagram-style
  • media-carousel (۵+) — اسلایدر با dots + counter
  • video — تامبنیل + play overlay + duration chip
  • audio — waveform + play (مناسب رادیو/voice-note)
  • وضعیت loading — shimmer placeholder
  • وضعیت failed — placeholder با دکمه‌ی تلاش مجدد
  • وضعیت unavailable — placeholder «در سامانه ذخیره نشده»
  • وضعیت expired — رسانه ضبط شده اما از بازه‌ی نگه‌داری خارج شده (مثلاً کلیپ‌های پخش ~۳۰ روزه)
  • source-removed — اعلان بایگانی + دکمه‌ی «نمایش بایگانی»
  • sensitivity (nsfw/graphic/spoiler) — بلر با reveal کلیکی

راهنمای استفاده

بکنید

  • context را با جایگاه واقعی رندر ست کنید: compact برای ردیف‌های فشرده، comfortable برای فید، detail برای درآور جزئیات — انتخاب کراپ (cover) یا letterbox + بک‌دراپ بلور از همین prop می‌آید.
  • برای هر PostMediaItem متن alt توصیفی بدهید — رندرر آن را مستقیم روی <img alt> می‌گذارد و در نبودش alt="" رندر می‌شود؛ یعنی تصویر برای صفحه‌خوان عملاً نامرئی است.
  • onRetry را پاس بدهید تا آیتم‌های status: 'failed' دکمه «تلاش مجدد» داشته باشند — بدون آن placeholder بدون CTA رندر می‌شود.
  • نسبت ابعادی طبیعی (aspectRatio) هر آیتم را پاس بدهید و کراپ/letterbox را به کامپوننت بسپارید.
  • برای حساسیت سطح پست (PostFlags.sensitive) از sensitiveOverride استفاده کنید تا کل ناحیه‌ی رسانه یک‌جا blur شود؛ حساسیت تک‌آیتمی را روی item.sensitivity بگذارید.

نکنید

  • آیتم‌های بیش از ۴ تا را برای گرید خودتان slice نکنید — گرید چهار تای اول را نشان می‌دهد و روی سلول آخر چیپ «+N» می‌گذارد.
  • وضعیت reveal محتوای حساس را بیرون از کامپوننت نگه ندارید — رضایت reveal به ازای هر مدیا است و با تغییر item.url عمداً ریست می‌شود؛ state موازی این قرارداد را می‌شکند.
  • به‌جای status: 'failed' یا 'unavailable'، تگ <img> شکسته یا placeholder دست‌ساز رندر نکنید.
  • expired را با unavailable جایگزین نکنید — expired یعنی رسانه ضبط شده بود و از بازه‌ی نگه‌داری خارج شده است؛ متن اعلان این دو عمداً متفاوت است.
  • تصویر حساس را با thumbnailUrl در کامپوننت دیگری بدون blur نمایش ندهید — PostMediaThumb همین قرارداد blur را در سایز کوچک هم اجرا می‌کند و reveal در آن سایز عمداً وجود ندارد.

Props

<PostMedia> علاوه بر props زیر، همه‌ی React.HTMLAttributes<HTMLDivElement> (مثل className) را می‌پذیرد. ثابت‌های CENTERED_CONTAINER_CLS، FOREGROUND_IMG_CLS و SENSITIVITY_LABEL نیز export می‌شوند تا الگوی مرکزچینی رسانه و برچسب‌های حساسیت در مصرف‌کننده‌ها یکسان بماند.

PostMedia

Prop

Type

PostMediaSingle

Prop

Type

PostMediaGrid

Prop

Type

PostMediaCarousel

Prop

Type

PostMediaVideo

Prop

Type

PostMediaAudio

Prop

Type

PostMediaStory

Prop

Type

PostMediaHighlight

Prop

Type

PostMediaPlaceholder

Prop

Type

PostMediaSourceRemoved

Prop

Type

PostMediaSensitiveOverlay

Prop

Type

PostMediaThumb

Prop

Type

KeywordSnippet

Prop

Type

BlurBackdrop

Prop

Type

دسترسی‌پذیری

جریان متن جایگزین (alt)

  • PostMediaItem.alt مستقیم روی <img alt> می‌نشیند. اگر ست نشود، alt="" رندر می‌شود تا صفحه‌خوان URL تصویر را نخواند — پس برای تصاویر informative حتماً alt توصیفی بدهید.
  • لایه‌های decorative همیشه از درخت دسترسی خارج هستند: بک‌دراپ بلور (aria-hidden="true" + alt="")، میله‌ها/تصویر waveform، dot indicator های کاروسل، و آیکن بزرگ‌نمایی روی هاور.

محتوای حساس (blur + reveal)

  • overlay مربوط به reveal یک <button type="button"> واقعی است با aria-label «نمایش محتوای حساس / محتوای تصویری شدید / افشاگر داستان» — با Tab فوکوس می‌گیرد و با Enter یا Space فعال می‌شود.
  • در حالت sensitiveOverride، ناحیه‌ی blur شده aria-hidden="true" و pointer-events-none دارد — صفحه‌خوان فقط دکمه‌ی reveal را می‌بیند و محتوای زیر blur افشا نمی‌شود.
  • reveal به ازای هر مدیا نگه داشته می‌شود و با تغییر item.url (مثلاً ناوبری j/k در درآور) ریست می‌شود — رضایت کاربر به مدیای بعدی نشت نمی‌کند.
  • reveal یک‌طرفه است: دکمه‌ای برای blur مجدد وجود ندارد؛ بازگشت به حالت blur تنها با تغییر مدیا اتفاق می‌افتد.
  • در سایز thumbnail (PostMediaThumb) reveal عمداً وجود ندارد — فقط blur + آیکن قفل؛ نمایش محتوای حساس به نمای جزئیات واگذار می‌شود.

کاروسل و هایلایت

  • فلش‌های قبلی/بعدی کاروسل دکمه‌های واقعی با aria-label="اسلاید قبلی" / aria-label="اسلاید بعدی" هستند، در دو انتها disabled می‌شوند و با focus-visible حتی بدون هاور ظاهر می‌شوند.
  • جهت فلش‌ها از جهت واقعی سند (useDocumentDirection) خوانده می‌شود، نه از variant rtl: — در پرتال‌های Radix و iframe ها هم درست می‌ماند.
  • dot indicator ها decorative (aria-hidden="true") هستند؛ موقعیت اسلاید با چیپ متنی «X از Y» (ارقام فارسی) منتقل می‌شود.
  • فلش‌های پیمایش هایلایت نیز aria-label="استوری قبلی" / aria-label="استوری بعدی" دارند و در ابتدا و انتهای مجموعه disabled می‌شوند.

وضعیت‌ها و پخش

  • placeholder بارگذاری role="status" و aria-busy="true" با aria-label="در حال بارگذاری تصویر" دارد — صفحه‌خوان وضعیت را اعلام می‌کند.
  • دکمه‌ی play صوت aria-label پویا دارد («پخش صوت» / «توقف پخش») و بدون URL قابل‌پخش disabled می‌شود.
  • ویدیو در نمای جزئیات <video controls> بومی است — کنترل کیبوردی مرورگر (Space، فلش‌ها) کار می‌کند. مدیای خزیده‌شده caption track ندارد؛ رونوشت در تب جداگانه‌ی درآور ارائه می‌شود.

تعامل با کیبورد

  • Tab: انتقال فوکوس بین کنترل‌های تعاملی — دکمه reveal محتوای حساس، فلش‌های کاروسل/هایلایت، دکمه play صوت، «تلاش مجدد»، و کنترل‌های <video> - Enter یا Space: فعال‌سازی کنترل فوکوس‌شده (reveal، پخش/توقف، پیمایش اسلاید) - فلش‌های کاروسل روی فوکوس کیبورد (focus-visible) نمایان می‌شوند، حتی بدون هاور - کنترل‌های disabled (انتهای کاروسل، صوت بدون URL) از ترتیب فعال‌سازی خارج می‌شوند

کامپوننت‌های مرتبط

  • اگر فقط متن نمایش می‌دهید → از <PostBody> خود <PostCard> استفاده کنید
  • اگر می‌خواهید چندین کارت در فید نمایش دهید → از <PostList> به‌جای رندر مستقیم <PostMedia>