Update blog editor features
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import DateObject from 'react-date-object'
|
||||
import gregorian from 'react-date-object/calendars/gregorian'
|
||||
import persian from 'react-date-object/calendars/persian'
|
||||
import persian_fa from 'react-date-object/locales/persian_fa'
|
||||
|
||||
/** قالب رسمی نمایش تاریخ جلالی — تقویم شمسی و اعداد فارسی */
|
||||
export const JALALI_DATE_FORMATTER = new Intl.DateTimeFormat('fa-IR', {
|
||||
dateStyle: 'medium',
|
||||
calendar: 'persian',
|
||||
})
|
||||
|
||||
/** ساعت به وقت جلالی (فارسی) */
|
||||
export const JALALI_TIME_FORMATTER = new Intl.DateTimeFormat('fa-IR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
|
||||
const ISO_DATE = /^\d{4}-\d{2}-\d{2}([T\s].*)?$/
|
||||
|
||||
/**
|
||||
* نمایش هر مقدار تاریخ به شکل جلالی (مثل «۲۲ شهریور ۱۴۰۵»).
|
||||
* رشتههایی که تاریخ نیستند (مثل تاریخ شمسی دستنویس) دستنخورده برمیگردند.
|
||||
*/
|
||||
export function formatJalali(value?: string | number | Date | null, fallback = ''): string {
|
||||
if (value === null || value === undefined || value === '') return fallback
|
||||
|
||||
if (value instanceof Date) {
|
||||
return Number.isNaN(value.getTime()) ? fallback : JALALI_DATE_FORMATTER.format(value)
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
const fromNumber = new Date(value)
|
||||
return Number.isNaN(fromNumber.getTime()) ? fallback : JALALI_DATE_FORMATTER.format(fromNumber)
|
||||
}
|
||||
|
||||
const raw = String(value).trim()
|
||||
if (!raw) return fallback
|
||||
|
||||
if (ISO_DATE.test(raw)) {
|
||||
const parsed = new Date(raw)
|
||||
if (!Number.isNaN(parsed.getTime())) return JALALI_DATE_FORMATTER.format(parsed)
|
||||
}
|
||||
|
||||
return raw
|
||||
}
|
||||
|
||||
/** رشته ISO میلادی ذخیرهشده (YYYY-MM-DD) → DateObject شمسی برای انتخابگر تاریخ */
|
||||
export function toJalaliDateObject(value?: string | null): DateObject | null {
|
||||
const raw = (value ?? '').trim()
|
||||
if (!raw) return null
|
||||
|
||||
if (ISO_DATE.test(raw)) {
|
||||
const parsed = new DateObject({ date: raw })
|
||||
if (parsed.isValid) return parsed.convert(persian, persian_fa)
|
||||
}
|
||||
|
||||
// تاریخهای شمسی دستنویس مثل ۱۴۰۳/۰۵/۱۲
|
||||
const manual = new DateObject({ date: raw, calendar: persian, locale: persian_fa })
|
||||
return manual.isValid ? manual : null
|
||||
}
|
||||
|
||||
/** انتخاب انتخابگر تاریخ → رشته ISO میلادی برای ذخیره در دیتابیس */
|
||||
export function toISODate(value?: DateObject | Date | null): string {
|
||||
if (!value) return ''
|
||||
const asObject = value instanceof Date ? new DateObject({ date: value }) : value
|
||||
const gregorianDate = asObject.convert(gregorian)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${gregorianDate.year}-${pad(gregorianDate.month.number)}-${pad(gregorianDate.day)}`
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import slugify from 'slugify'
|
||||
|
||||
/**
|
||||
* تبدیل متن (فارسی یا انگلیسی) به شناسه/لنگر امن.
|
||||
* slugify حروف فارسی و عربی را به معادل لاتین ترانویسی میکند تا لنگرهای
|
||||
* فهرست مطالب در همه مرورگرها ASCII، پایدار و قابل اشتراکگذاری بمانند.
|
||||
*/
|
||||
export function slugifyFa(input: string): string {
|
||||
const normalized = (input ?? '')
|
||||
.toString()
|
||||
.normalize('NFC')
|
||||
// نیمفاصله، اتصالدهنده و نویسههای نامرئی جهت → فاصله
|
||||
.replace(/[\u200b-\u200f\u202a-\u202e]/g, ' ')
|
||||
// یکسانسازی حروف عربی با معادل فارسی
|
||||
.replace(/[يى]/g, 'ی')
|
||||
.replace(/ك/g, 'ک')
|
||||
.replace(/[ۀة]/g, 'ه')
|
||||
.trim()
|
||||
|
||||
const slug = slugify(normalized, {
|
||||
lower: true,
|
||||
trim: true,
|
||||
replacement: '-',
|
||||
// فقط حروف/اعداد/فاصله/خط تیره بماند — بقیه نشانهها حذف شوند
|
||||
remove: /[^\w\s-]/g,
|
||||
})
|
||||
|
||||
return slug.replace(/-{2,}/g, '-').replace(/^-+|-+$/g, '')
|
||||
}
|
||||
|
||||
/** ساخت شناسه یکتا — تیترهای تکراری پسوند عددی میگیرند (مثل about، about-2) */
|
||||
export function uniqueSlug(base: string, used: Map<string, number>): string {
|
||||
const key = base || 'section'
|
||||
const count = used.get(key) ?? 0
|
||||
used.set(key, count + 1)
|
||||
return count === 0 ? key : `${key}-${count + 1}`
|
||||
}
|
||||
Reference in New Issue
Block a user