import { useMemo } from '@wordpress/element'; import type { SourceOption } from './editorGlobals'; import { ACCENT_THEME_PRESETS, DEFAULT_ACCENT_THEME_KEY } from './accentThemes'; /** * Normalize a user-entered amount string: * strip whitespace, treat comma as decimal separator when unambiguous. */ export const normalizeAmountInput = (value: string): string => { const compact = value.trim().replace(/\s+/g, ''); if (compact === '') return ''; const commaCount = (compact.match(/,/g) || []).length; const dotCount = (compact.match(/\./g) || []).length; if (commaCount > 0 && dotCount > 0) { const lastComma = compact.lastIndexOf(','); const lastDot = compact.lastIndexOf('.'); if (lastComma > lastDot) { // European: 1.234,56 → comma is decimal return compact.replace(/\./g, '').replace(',', '.'); } // US/UK: 1,234.56 → dot is decimal return compact.replace(/,/g, ''); } if (commaCount === 1) return compact.replace(',', '.'); if (commaCount > 1) return compact.replace(/,/g, ''); return compact; }; /** * Hook: available currency codes for the current source+deal pair. */ export function useAvailableCodes(sourceId: string, deal: string): Set | undefined { return useMemo(() => { const map = window.crtodaySourceCurrencies ?? {}; const codes = map[`${sourceId}:${deal}`]; return Array.isArray(codes) ? new Set(codes) : undefined; }, [sourceId, deal]); } /** * Resolve the accent theme preset matching the given key. */ export function resolveSelectedTheme(accentTheme: string) { return ( ACCENT_THEME_PRESETS.find((preset) => preset.key === accentTheme) ?? ACCENT_THEME_PRESETS.find((preset) => preset.key === DEFAULT_ACCENT_THEME_KEY) ?? ACCENT_THEME_PRESETS[0] ); } /** * Resolve a human-readable source label from the localized source options. */ export function resolveSourceLabel(sourceId: string, deal: string): string { const sources: SourceOption[] = window.crtodaySourceOptions ?? []; const key = `${sourceId}:${deal}`; const match = sources.find((s) => s.id === key); if (match) { return match.label.replace(/\s*\([A-Z]{3}\)$/, ''); } return sourceId; } /** * Build grouped options from localized source data. */ export function buildSourceOptions( editorLabels: Record ): Array<{ label: string; value: string }> { const sources: SourceOption[] = window.crtodaySourceOptions ?? []; const builtinGroup = sources.filter((s) => s.group === 'builtin'); const localGroup = sources.filter((s) => s.group === 'local'); const options: Array<{ label: string; value: string }> = []; if (builtinGroup.length > 0) { options.push({ label: `── ${editorLabels.sourceGroupBuiltIn ?? ''} ──`, value: '' }); builtinGroup.forEach((s) => options.push({ label: s.label, value: s.id })); } if (localGroup.length > 0) { options.push({ label: `── ${editorLabels.sourceGroupLocal ?? ''} ──`, value: '' }); localGroup.forEach((s) => options.push({ label: s.label, value: s.id })); } return options; } /** * Parse a compound source value ("FEX:STANDARD") into sourceId + deal. */ export function parseSourceValue(compound: string): { sourceId: string; deal: string } { const [sourceId, ...dealParts] = compound.split(':'); return { sourceId, deal: dealParts.join(':') || 'STANDARD' }; }