mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-27 01:22:49 +00:00
Options editor used a single toggle-based bilingual field (BilingualInput) that swapped English/Amharic in place via a tiny, easy-to-miss button. Authors were filling English only and never noticing Amharic was empty. - QuestionOptionsEditor: show separate always-visible English/Amharic TextInputs per option instead of the toggle field. - BilingualInput (shared): for other callers still using the toggle, add a tooltip and a red dot indicator when the hidden language is empty, so the gap is visible without switching. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
Tooltip,
|
|
TextInput,
|
|
UnstyledButton,
|
|
rem,
|
|
type TextInputProps,
|
|
} from '@mantine/core';
|
|
|
|
export interface BilingualValue {
|
|
en: string;
|
|
am: string;
|
|
}
|
|
|
|
interface BilingualInputProps
|
|
extends Omit<TextInputProps, 'value' | 'onChange' | 'placeholder' | 'rightSection' | 'rightSectionWidth'> {
|
|
value: BilingualValue;
|
|
onChange: (value: BilingualValue) => void;
|
|
placeholder?: string | BilingualValue;
|
|
}
|
|
|
|
function resolvePlaceholder(placeholder: string | BilingualValue | undefined, lang: 'en' | 'am'): string {
|
|
if (!placeholder) {
|
|
return lang === 'en' ? 'Enter in English' : 'በአማርኛ ያስገቡ';
|
|
}
|
|
if (typeof placeholder === 'string') {
|
|
return placeholder;
|
|
}
|
|
return placeholder[lang];
|
|
}
|
|
|
|
export function BilingualInput({
|
|
label,
|
|
value,
|
|
onChange,
|
|
required,
|
|
placeholder,
|
|
...rest
|
|
}: BilingualInputProps) {
|
|
const [lang, setLang] = useState<'en' | 'am'>('en');
|
|
const otherLang = lang === 'en' ? 'am' : 'en';
|
|
const otherLangName = otherLang === 'en' ? 'English' : 'Amharic';
|
|
const otherIsEmpty = !value[otherLang]?.trim();
|
|
|
|
const toggle = () => setLang((l) => (l === 'en' ? 'am' : 'en'));
|
|
|
|
return (
|
|
<TextInput
|
|
label={label}
|
|
required={required}
|
|
placeholder={resolvePlaceholder(placeholder, lang)}
|
|
value={value[lang]}
|
|
onChange={(e) => onChange({ ...value, [lang]: e.currentTarget.value })}
|
|
rightSection={
|
|
<Tooltip
|
|
label={`Switch to ${otherLangName}${otherIsEmpty ? ' — empty' : ''}`}
|
|
withArrow
|
|
>
|
|
<UnstyledButton
|
|
onClick={toggle}
|
|
aria-label={`Switch to ${otherLangName}`}
|
|
style={{
|
|
position: 'relative',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: rem(32),
|
|
height: rem(24),
|
|
borderRadius: rem(4),
|
|
fontSize: rem(11),
|
|
fontWeight: 700,
|
|
letterSpacing: '0.05em',
|
|
background:
|
|
lang === 'en'
|
|
? 'var(--mantine-color-blue-1)'
|
|
: 'var(--mantine-color-teal-1)',
|
|
color:
|
|
lang === 'en'
|
|
? 'var(--mantine-color-blue-7)'
|
|
: 'var(--mantine-color-teal-7)',
|
|
cursor: 'pointer',
|
|
transition: 'background 150ms ease',
|
|
}}
|
|
>
|
|
{lang === 'en' ? 'EN' : 'AM'}
|
|
{otherIsEmpty && (
|
|
<span
|
|
aria-hidden
|
|
title={`${otherLangName} text is missing`}
|
|
style={{
|
|
position: 'absolute',
|
|
top: -2,
|
|
right: -2,
|
|
width: rem(7),
|
|
height: rem(7),
|
|
borderRadius: '50%',
|
|
background: 'var(--mantine-color-red-6)',
|
|
border: '1px solid var(--mantine-color-body)',
|
|
}}
|
|
/>
|
|
)}
|
|
</UnstyledButton>
|
|
</Tooltip>
|
|
}
|
|
styles={{
|
|
input: {
|
|
paddingRight: rem(46),
|
|
},
|
|
}}
|
|
{...rest}
|
|
/>
|
|
);
|
|
}
|