2026-08-06 22:54:50 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* Liste görünümlerinde (grid hücresi, image upload/viewer editörleri) kullanılan
|
|
|
|
|
|
* ortak görsel değer normalizasyon yardımcıları.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Daha önce useListFormColumns.ts, ImageViewerEditorComponent.tsx ve
|
|
|
|
|
|
* ImageUploadEditorComponent.tsx içinde birebir tekrarlanıyordu.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
export const NO_IMAGE = '/img/others/no-image.png'
|
|
|
|
|
|
|
|
|
|
|
|
/** String ya da obje olabilen bir değeri düz objeye çevirir; dizi/geçersiz JSON için undefined döner. */
|
|
|
|
|
|
export const parseJsonObject = (value: unknown): Record<string, any> | undefined => {
|
|
|
|
|
|
if (!value) return undefined
|
|
|
|
|
|
if (typeof value === 'object') {
|
|
|
|
|
|
return Array.isArray(value) ? undefined : (value as Record<string, any>)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeof value !== 'string') return undefined
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(value)
|
|
|
|
|
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
|
|
|
|
? (parsed as Record<string, any>)
|
|
|
|
|
|
: undefined
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return undefined
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Pozitif sayıya çevirir, aksi halde varsayılanı döner. */
|
|
|
|
|
|
export const normalizeImageSize = (value: unknown, fallback: number) => {
|
|
|
|
|
|
const size = Number(value)
|
|
|
|
|
|
return Number.isFinite(size) && size > 0 ? size : fallback
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Obje formatındaki görsel değerinden (url/src/fileUrl/path/value) kaynağı çıkarır. */
|
|
|
|
|
|
export const getImageSource = (value: unknown) => {
|
|
|
|
|
|
if (!value) return ''
|
|
|
|
|
|
if (typeof value === 'string') return value.trim()
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof value === 'object') {
|
|
|
|
|
|
const item = value as Record<string, unknown>
|
|
|
|
|
|
return String(item.url ?? item.src ?? item.fileUrl ?? item.path ?? item.value ?? '').trim()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return String(value).trim()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const isProbablyBase64Image = (value: string) =>
|
|
|
|
|
|
value.length > 80 && /^[A-Za-z0-9+/]+={0,2}$/.test(value)
|
|
|
|
|
|
|
|
|
|
|
|
/** Ham base64 içeriğini data URL'e çevirir; zaten URL ise dokunmaz. */
|
|
|
|
|
|
export const toImageSource = (value: string) => {
|
|
|
|
|
|
const trimmed = value.trim()
|
|
|
|
|
|
if (!trimmed) return ''
|
|
|
|
|
|
if (trimmed.startsWith('data:image/') || !isProbablyBase64Image(trimmed)) return trimmed
|
|
|
|
|
|
return `data:image/jpeg;base64,${trimmed}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const splitImageString = (value: string) => {
|
|
|
|
|
|
const trimmed = value.trim()
|
|
|
|
|
|
if (!trimmed) return []
|
2026-10-04 08:14:38 +00:00
|
|
|
|
// data URI virgul tasir (data:application/pdf;base64,...); ayraclarla bolunmez.
|
|
|
|
|
|
if (trimmed.startsWith('data:')) return [trimmed]
|
2026-08-06 22:54:50 +00:00
|
|
|
|
|
|
|
|
|
|
return trimmed
|
|
|
|
|
|
.split(/\r?\n|\|\s*/)
|
|
|
|
|
|
.flatMap((part) => {
|
|
|
|
|
|
const text = part.trim()
|
2026-10-04 08:14:38 +00:00
|
|
|
|
if (!text || text.startsWith('data:')) return text ? [text] : []
|
2026-08-06 22:54:50 +00:00
|
|
|
|
return text.split(',').map((item) => item.trim())
|
|
|
|
|
|
})
|
|
|
|
|
|
.map(toImageSource)
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Tek görsel, çoklu görsel, JSON dizi ya da ayraçlı string olabilen değeri
|
|
|
|
|
|
* gösterilebilir URL listesine dönüştürür.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const normalizeImageValue = (value: unknown): string[] => {
|
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
|
return value.flatMap((item) => normalizeImageValue(item))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
|
const trimmed = value.trim()
|
|
|
|
|
|
if (!trimmed) return []
|
|
|
|
|
|
|
|
|
|
|
|
if (trimmed.startsWith('[')) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(trimmed)
|
|
|
|
|
|
if (Array.isArray(parsed)) {
|
|
|
|
|
|
return parsed.flatMap((item) => normalizeImageValue(item))
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return [toImageSource(trimmed)]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return splitImageString(trimmed)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const source = getImageSource(value)
|
|
|
|
|
|
return source ? [toImageSource(source)] : []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Kolon yapılandırmasından image upload/viewer seçeneklerini çözer. */
|
|
|
|
|
|
export const resolveImageColumnOptions = (column: unknown): Record<string, any> => {
|
|
|
|
|
|
const col = column as any
|
|
|
|
|
|
const configured = col?.extras?.imageUploadOptions
|
|
|
|
|
|
if (configured && Object.keys(configured).length > 0) {
|
|
|
|
|
|
return configured
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return parseJsonObject(col?.extras?.editorOptions) ?? parseJsonObject(col?.editorOptions) ?? {}
|
|
|
|
|
|
}
|