You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
3.9 KiB
97 lines
3.9 KiB
import { clsx, type ClassValue } from 'clsx'
|
|
import { twMerge } from 'tailwind-merge'
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs))
|
|
}
|
|
|
|
const AR_FA_TO_EN_DIGITS: Record<string, string> = {
|
|
'۰': '0', '۱': '1', '۲': '2', '۳': '3', '۴': '4',
|
|
'۵': '5', '۶': '6', '۷': '7', '۸': '8', '۹': '9',
|
|
'٠': '0', '١': '1', '٢': '2', '٣': '3', '٤': '4',
|
|
'٥': '5', '٦': '6', '٧': '7', '٨': '8', '٩': '9',
|
|
}
|
|
|
|
/**
|
|
* تبدیل تمامی ارقام فارسی و عربی به ارقام استاندارد انگلیسی
|
|
*/
|
|
export const toEn = (n: number | string | null | undefined): string => {
|
|
if (n == null) return ''
|
|
return String(n).replace(/[۰-۹٠-٩]/g, (d) => AR_FA_TO_EN_DIGITS[d] ?? d)
|
|
}
|
|
|
|
/**
|
|
* طبق استاندارد پنل عقیله، کلیه اعداد در سراسر پنل به صورت انگلیسی نمایش داده میشوند.
|
|
* این تابع به عنوان آداپتور به toEn متصل است تا هیچ کدی نشکند و ارقام انگلیسی تولید شوند.
|
|
*/
|
|
export const toFa = toEn
|
|
|
|
/**
|
|
* استخراج URL تصویر از thumbnailِ خامِ بکاند.
|
|
* ورودی میتواند رشته باشد یا آبجکتی مثل { sm, md, lg } یا { thumb, medium, large }.
|
|
*/
|
|
export function thumbnailUrl(thumbnail: unknown): string | null {
|
|
if (!thumbnail) return null
|
|
if (typeof thumbnail === 'string') return thumbnail.trim() || null
|
|
if (typeof thumbnail === 'object') {
|
|
const o = thumbnail as Record<string, unknown>
|
|
const v = o.md ?? o.medium ?? o.lg ?? o.large ?? o.sm ?? o.thumb ?? Object.values(o)[0]
|
|
return v != null ? String(v).trim() || null : null
|
|
}
|
|
return null
|
|
}
|
|
|
|
export const REMOTE_MEDIA_ORIGIN = 'https://aqila.newhorizonco.uk'
|
|
|
|
/**
|
|
* دریافت آدرس جایگزین از سرور اصلی ریموت در صورتی که تصویر در لوکال هاست موجود نباشد
|
|
*/
|
|
export function getMediaFallbackUrl(url: string | null | undefined): string | null {
|
|
if (!url) return null
|
|
const trimmed = String(url).trim()
|
|
if (!trimmed || trimmed.startsWith('data:') || trimmed.startsWith('blob:')) return null
|
|
if (trimmed.startsWith(REMOTE_MEDIA_ORIGIN)) return trimmed
|
|
|
|
const cleanPath = trimmed
|
|
.replace(/^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?\//i, '')
|
|
.replace(/^\/?(media\/)?/, '')
|
|
|
|
return `${REMOTE_MEDIA_ORIGIN}/media/${cleanPath}`
|
|
}
|
|
|
|
/**
|
|
* استانداردسازی آدرس تصاویر و فایلهای چندرسانهای
|
|
* آدرسهای نسبی، فاقد پروتکل یا لوکالهاست را به آدرس معتبر و قابل رندر تبدیل میکند.
|
|
*/
|
|
export function resolveMediaUrl(url: string | null | undefined): string {
|
|
if (!url) return ''
|
|
const trimmed = String(url).trim()
|
|
if (!trimmed) return ''
|
|
if (trimmed.startsWith('data:') || trimmed.startsWith('blob:')) {
|
|
return trimmed
|
|
}
|
|
// تبدیل URL کامل لوکالهاست به مسیر نسبی تا از طریق پراکسیِ Vite بدون خطای CORS بارگذاری شود
|
|
const localMatch = trimmed.match(/^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?\/(media|static)\/(.*)$/i)
|
|
if (localMatch && localMatch[1] && localMatch[2]) {
|
|
return `/${localMatch[1]}/${localMatch[2]}`
|
|
}
|
|
if (/^https?:\/\//i.test(trimmed)) {
|
|
return trimmed
|
|
}
|
|
if (trimmed.startsWith('media/') || trimmed.startsWith('static/')) {
|
|
return `/${trimmed}`
|
|
}
|
|
if (trimmed.startsWith('factors/')) {
|
|
return `/media/${trimmed}`
|
|
}
|
|
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`
|
|
}
|
|
|
|
|
|
/** ثانیه → «م:ثث» با ارقام فارسی (مثلاً ۳:۰۵). مقادیر نامعتبر → ۰:۰۰ */
|
|
export function formatTime(seconds: number): string {
|
|
if (!Number.isFinite(seconds) || seconds < 0) seconds = 0
|
|
const m = Math.floor(seconds / 60)
|
|
const s = Math.floor(seconds % 60)
|
|
return toFa(`${m}:${String(s).padStart(2, '0')}`)
|
|
}
|