import { t } from '@/i18n' import type { ImageVariant, NewVariantRequest, VariantDefinition, VariantFit, VariantFormat, } from '@/composables/useImageVariants' export const NAME_MAX_LENGTH = 32 const DIMENSION_MIN = 1 const DIMENSION_MAX = 8000 export const QUALITY_MIN = 1 export const QUALITY_MAX = 100 const DEFAULT_QUALITY = 80 const NAME_PATTERN = /^[a-z0-9-]{2,32}$/ const WHOLE_NUMBER = /^\d+$/ export type FieldName = 'name' | 'width' | 'height' | 'quality' export interface VariantFormValues { name: string width: string height: string fit: VariantFit format: VariantFormat quality: string generateOnUpload: boolean enabled: boolean } export type FieldErrors = Partial> & { dimension?: string } export const FIELD_ORDER: FieldName[] = ['name', 'width', 'height', 'quality'] export const VARIANT_TEMPLATES = [ { name: 'thumb', width: 400, height: 300, quality: 80 }, { name: 'hero', width: 1200, height: 630, quality: 82 }, ] as const export function blankValues(): VariantFormValues { return { name: '', width: '', height: '', fit: 'cover', format: 'original', quality: String(DEFAULT_QUALITY), generateOnUpload: false, enabled: true, } } export function valuesFromTemplate( template: (typeof VARIANT_TEMPLATES)[number], ): VariantFormValues { return { ...blankValues(), name: template.name, width: String(template.width), height: String(template.height), format: 'webp', quality: String(template.quality), } } export function valuesFromVariant(variant: ImageVariant): VariantFormValues { return { name: variant.name, width: variant.width === null ? '' : String(variant.width), height: variant.height === null ? '' : String(variant.height), fit: variant.fit, format: variant.format, quality: String(variant.quality ?? DEFAULT_QUALITY), generateOnUpload: variant.generate_on_upload, enabled: variant.enabled, } } export function hasChanged(current: VariantFormValues, initial: VariantFormValues) { return (Object.keys(initial) as (keyof VariantFormValues)[]).some( (key) => current[key] !== initial[key], ) } export function usesQuality(format: VariantFormat) { return format !== 'png' } function wholeNumberInRange(text: string, min: number, max: number) { const trimmed = text.trim() if (!WHOLE_NUMBER.test(trimmed)) { return null } const value = Number(trimmed) return value >= min && value <= max ? value : null } export function parseDimension(text: string) { return wholeNumberInRange(text, DIMENSION_MIN, DIMENSION_MAX) } function parseQuality(text: string) { return wholeNumberInRange(text, QUALITY_MIN, QUALITY_MAX) } export function validateField(field: FieldName, values: VariantFormValues): string | undefined { if (field === 'name') { return NAME_PATTERN.test(values.name) ? undefined : t('Use lowercase letters, numbers and dashes, 2 to 32 characters.') } if (field === 'quality') { return !usesQuality(values.format) || parseQuality(values.quality) !== null ? undefined : t('Enter a value from 1 to 100.') } const text = values[field] return text.trim() === '' || parseDimension(text) !== null ? undefined : t('Enter a value from 1 to 8000.') } export function lacksDimension(values: VariantFormValues) { return values.width.trim() === '' && values.height.trim() === '' } export function sliderValue(text: string) { const parsed = Number.parseInt(text, 10) if (Number.isNaN(parsed)) { return QUALITY_MIN } return Math.min(QUALITY_MAX, Math.max(QUALITY_MIN, parsed)) } export function toDefinition(values: VariantFormValues): VariantDefinition { const definition: VariantDefinition = { width: parseDimension(values.width), height: parseDimension(values.height), fit: values.fit, format: values.format, generate_on_upload: values.generateOnUpload, enabled: values.enabled, } if (usesQuality(values.format)) { definition.quality = parseQuality(values.quality) ?? DEFAULT_QUALITY } return definition } export function toNewVariant(values: VariantFormValues): NewVariantRequest { return { name: values.name, ...toDefinition(values) } }