chore: update French i18n (#5561)
This commit is contained in:
1 parent
ba140a05f7
commit
181c0f32c5
185 files changed
+2779
-374
No files matched your search
@@ -1,6 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { DayPicker } from 'react-day-picker';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { format } from 'date-fns';
|
||||
import type { Locale } from 'date-fns';
|
||||
import {
|
||||
ar as arLocale,
|
||||
ca as caLocale,
|
||||
de as deLocale,
|
||||
enUS,
|
||||
fr as frLocale,
|
||||
ja as jaLocale,
|
||||
nl as nlLocale,
|
||||
ptBR as ptBRLocale,
|
||||
ru as ruLocale,
|
||||
tr as trLocale,
|
||||
vi as viLocale,
|
||||
zhCN as zhLocale,
|
||||
} from 'date-fns/locale';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
@@ -8,7 +26,37 @@ import { buttonVariants } from '../Button';
|
||||
|
||||
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
||||
|
||||
const DATE_FNS_LOCALE_MAP: Record<string, Locale> = {
|
||||
en: enUS,
|
||||
'en-US': enUS,
|
||||
fr: frLocale,
|
||||
'fr-FR': frLocale,
|
||||
ar: arLocale,
|
||||
ca: caLocale,
|
||||
de: deLocale,
|
||||
'ja-JP': jaLocale,
|
||||
ja: jaLocale,
|
||||
nl: nlLocale,
|
||||
'pt-BR': ptBRLocale,
|
||||
pt: ptBRLocale,
|
||||
ru: ruLocale,
|
||||
'tr-TR': trLocale,
|
||||
tr: trLocale,
|
||||
vi: viLocale,
|
||||
zh: zhLocale,
|
||||
'zh-CN': zhLocale,
|
||||
'zh-cn': zhLocale,
|
||||
'test-LNG': enUS,
|
||||
};
|
||||
|
||||
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
|
||||
const { i18n, t } = useTranslation('DatePicker');
|
||||
|
||||
const locale = useMemo(() => {
|
||||
const lang = i18n.language || 'en';
|
||||
return DATE_FNS_LOCALE_MAP[lang] ?? enUS;
|
||||
}, [i18n.language]);
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
@@ -20,6 +68,10 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
||||
labelMonthDropdown: () => undefined,
|
||||
labelYearDropdown: () => undefined,
|
||||
}}
|
||||
locale={locale}
|
||||
formatters={{
|
||||
formatCaption: month => format(month, 'LLLL yyyy', { locale }),
|
||||
}}
|
||||
classNames={{
|
||||
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
|
||||
month: 'space-y-4',
|
||||
@@ -30,7 +82,7 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
||||
nav: 'space-x-1 flex items-center',
|
||||
table: 'w-full border-collapse space-y-1',
|
||||
head_row: 'flex',
|
||||
head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]',
|
||||
head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem] uppercase',
|
||||
row: 'flex w-full mt-2',
|
||||
cell: 'h-9 w-9 text-center text-base p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
|
||||
day: cn(
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { Icons } from '../../components/Icons/Icons';
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from '../../components/Tooltip/Tooltip';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* DataRow is a complex UI component that displays a selectable, interactive row with hierarchical data.
|
||||
@@ -135,6 +136,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const { t } = useTranslation('DataRow');
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const isTitleLong = title?.length > 25;
|
||||
|
||||
@@ -312,7 +314,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
className={`h-6 w-6 transition-opacity ${
|
||||
isSelected || !isVisible ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
|
||||
}`}
|
||||
aria-label={isVisible ? 'Hide' : 'Show'}
|
||||
aria-label={isVisible ? t('Hide') : t('Show')}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onToggleVisibility(e);
|
||||
@@ -361,7 +363,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
className="pl-2"
|
||||
data-cy="Rename"
|
||||
>
|
||||
Rename
|
||||
{t('Rename')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
{onCopy && (
|
||||
@@ -371,7 +373,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
className="pl-2"
|
||||
data-cy="Duplicate"
|
||||
>
|
||||
Duplicate
|
||||
{t('Duplicate')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
@@ -381,7 +383,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
className="pl-2"
|
||||
data-cy="Delete"
|
||||
>
|
||||
Delete
|
||||
{t('Delete')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
{onColor && (
|
||||
@@ -391,7 +393,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
className="pl-2"
|
||||
data-cy="Change Color"
|
||||
>
|
||||
Change Color
|
||||
{t('Change Color')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
@@ -401,7 +403,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
className="pl-2"
|
||||
data-cy="LockToggle"
|
||||
>
|
||||
{isLocked ? 'Unlock' : 'Lock'}
|
||||
{isLocked ? t('Unlock') : t('Lock')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { format, parse, isValid } from 'date-fns';
|
||||
import { Calendar as CalendarIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Calendar } from '../Calendar';
|
||||
import * as Popover from '../Popover';
|
||||
@@ -23,6 +24,7 @@ export function DatePickerWithRange({
|
||||
onChange,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement> & DatePickerWithRangeProps) {
|
||||
const { t } = useTranslation('DatePicker');
|
||||
const [start, setStart] = React.useState<string>(
|
||||
startDate ? format(parse(startDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : ''
|
||||
);
|
||||
@@ -85,7 +87,7 @@ export function DatePickerWithRange({
|
||||
<input
|
||||
id={`${id}-start`}
|
||||
type="text"
|
||||
placeholder="Start date"
|
||||
placeholder={t('Start Date', 'Start date')}
|
||||
autoComplete="off"
|
||||
value={start}
|
||||
onChange={e => handleInputChange(e, 'start')}
|
||||
@@ -122,7 +124,7 @@ export function DatePickerWithRange({
|
||||
<input
|
||||
id={`${id}-end`}
|
||||
type="text"
|
||||
placeholder="End date"
|
||||
placeholder={t('End Date', 'End date')}
|
||||
autoComplete="off"
|
||||
value={end}
|
||||
onChange={e => handleInputChange(e, 'end')}
|
||||
|
||||
@@ -11,7 +11,6 @@ export const Patient = (props: IconProps) => (
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
{...props}
|
||||
>
|
||||
<title>icon-patient</title>
|
||||
<g
|
||||
id="Production"
|
||||
stroke="none"
|
||||
|
||||
@@ -10,7 +10,6 @@ export const ToolLayout = (props: IconProps) => (
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>tool-layout</title>
|
||||
<g
|
||||
id="tool-layout"
|
||||
stroke="none"
|
||||
@@ -394,7 +393,6 @@ export const ToolBidirectional = (props: IconProps) => (
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>tool-bidirectional</title>
|
||||
<g
|
||||
id="tool-bidirectional"
|
||||
stroke="none"
|
||||
@@ -615,7 +613,6 @@ export const ToolCapture = (props: IconProps) => (
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>tool-</title>
|
||||
<g
|
||||
id="tool-"
|
||||
stroke="none"
|
||||
|
||||
@@ -60,7 +60,7 @@ const Body = () => {
|
||||
if (!data || data.length === 0) {
|
||||
return (
|
||||
<div className="text-primary-light mb-1 flex flex-1 items-center px-2 py-2 text-base">
|
||||
No tracked measurements
|
||||
{useTranslation('MeasurementTable').t('No tracked measurements')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Input } from '../Input/Input';
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../Select/Select';
|
||||
import { Switch } from '../Switch/Switch';
|
||||
@@ -143,6 +144,13 @@ interface ImageSizeProps {
|
||||
className?: string;
|
||||
maxWidth?: string;
|
||||
maxHeight?: string;
|
||||
/** Optional translation namespace. Defaults to 'CaptureViewportModal'. */
|
||||
translationNamespace?: string;
|
||||
/** Optional labels/placeholders to override translations */
|
||||
widthLabel?: string;
|
||||
heightLabel?: string;
|
||||
widthPlaceholder?: string;
|
||||
heightPlaceholder?: string;
|
||||
}
|
||||
|
||||
function ImageSize({
|
||||
@@ -154,7 +162,19 @@ function ImageSize({
|
||||
className,
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
translationNamespace = 'CaptureViewportModal',
|
||||
widthLabel,
|
||||
heightLabel,
|
||||
widthPlaceholder,
|
||||
heightPlaceholder,
|
||||
}: ImageSizeProps) {
|
||||
const { t } = useTranslation(translationNamespace);
|
||||
|
||||
// Use translations as defaults, but allow props to override
|
||||
const finalWidthLabel = widthLabel ?? t('Width', { defaultValue: 'Width' });
|
||||
const finalHeightLabel = heightLabel ?? t('Height', { defaultValue: 'Height' });
|
||||
const finalWidthPlaceholder = widthPlaceholder ?? t('Width', { defaultValue: 'Width' });
|
||||
const finalHeightPlaceholder = heightPlaceholder ?? t('Height', { defaultValue: 'Height' });
|
||||
return (
|
||||
<div className={cn('text-foreground space-y-1', className)}>
|
||||
<label className="block text-base">{children}</label>
|
||||
@@ -163,23 +183,23 @@ function ImageSize({
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* Width group */}
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-foreground text-base">W</span>
|
||||
<span className="text-foreground text-base">{finalWidthLabel}</span>
|
||||
<Input
|
||||
value={width}
|
||||
onChange={onWidthChange ?? (() => {})}
|
||||
placeholder="Width"
|
||||
placeholder={finalWidthPlaceholder}
|
||||
className="w-20"
|
||||
max={maxWidth}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Height group */}
|
||||
{/* Height/Length group */}
|
||||
<div className="text-foreground flex items-center space-x-2 text-base">
|
||||
<span className="text-foreground text-base">H</span>
|
||||
<span className="text-foreground text-base">{finalHeightLabel}</span>
|
||||
<Input
|
||||
value={height}
|
||||
onChange={onHeightChange ?? (() => {})}
|
||||
placeholder="Height"
|
||||
placeholder={finalHeightPlaceholder}
|
||||
className="w-20"
|
||||
max={maxHeight}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,114 @@
|
||||
import * as React from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Label } from '../Label';
|
||||
import { Input } from '../Input';
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const HOTKEY_TOKEN_TRANSLATIONS: Record<
|
||||
string,
|
||||
{
|
||||
i18nKey: string;
|
||||
defaultValue: string;
|
||||
}
|
||||
> = {
|
||||
ctrl: { i18nKey: 'HotkeyKeys.ctrl', defaultValue: 'Ctrl' },
|
||||
control: { i18nKey: 'HotkeyKeys.ctrl', defaultValue: 'Ctrl' },
|
||||
shift: { i18nKey: 'HotkeyKeys.shift', defaultValue: 'Shift' },
|
||||
alt: { i18nKey: 'HotkeyKeys.alt', defaultValue: 'Alt' },
|
||||
option: { i18nKey: 'HotkeyKeys.option', defaultValue: 'Option' },
|
||||
meta: { i18nKey: 'HotkeyKeys.meta', defaultValue: 'Cmd' },
|
||||
command: { i18nKey: 'HotkeyKeys.meta', defaultValue: 'Cmd' },
|
||||
cmd: { i18nKey: 'HotkeyKeys.meta', defaultValue: 'Cmd' },
|
||||
enter: { i18nKey: 'HotkeyKeys.enter', defaultValue: 'Enter' },
|
||||
return: { i18nKey: 'HotkeyKeys.enter', defaultValue: 'Enter' },
|
||||
esc: { i18nKey: 'HotkeyKeys.esc', defaultValue: 'Esc' },
|
||||
escape: { i18nKey: 'HotkeyKeys.esc', defaultValue: 'Esc' },
|
||||
space: { i18nKey: 'HotkeyKeys.space', defaultValue: 'Space' },
|
||||
spacebar: { i18nKey: 'HotkeyKeys.space', defaultValue: 'Space' },
|
||||
tab: { i18nKey: 'HotkeyKeys.tab', defaultValue: 'Tab' },
|
||||
backspace: { i18nKey: 'HotkeyKeys.backspace', defaultValue: 'Backspace' },
|
||||
delete: { i18nKey: 'HotkeyKeys.delete', defaultValue: 'Delete' },
|
||||
del: { i18nKey: 'HotkeyKeys.delete', defaultValue: 'Delete' },
|
||||
insert: { i18nKey: 'HotkeyKeys.insert', defaultValue: 'Insert' },
|
||||
ins: { i18nKey: 'HotkeyKeys.insert', defaultValue: 'Insert' },
|
||||
home: { i18nKey: 'HotkeyKeys.home', defaultValue: 'Home' },
|
||||
end: { i18nKey: 'HotkeyKeys.end', defaultValue: 'End' },
|
||||
pageup: { i18nKey: 'HotkeyKeys.pageup', defaultValue: 'Page Up' },
|
||||
pagedown: { i18nKey: 'HotkeyKeys.pagedown', defaultValue: 'Page Down' },
|
||||
up: { i18nKey: 'HotkeyKeys.up', defaultValue: 'Up Arrow' },
|
||||
down: { i18nKey: 'HotkeyKeys.down', defaultValue: 'Down Arrow' },
|
||||
left: { i18nKey: 'HotkeyKeys.left', defaultValue: 'Left Arrow' },
|
||||
right: { i18nKey: 'HotkeyKeys.right', defaultValue: 'Right Arrow' },
|
||||
capslock: { i18nKey: 'HotkeyKeys.capslock', defaultValue: 'Caps Lock' },
|
||||
plus: { i18nKey: 'HotkeyKeys.plus', defaultValue: 'Plus' },
|
||||
minus: { i18nKey: 'HotkeyKeys.minus', defaultValue: 'Minus' },
|
||||
comma: { i18nKey: 'HotkeyKeys.comma', defaultValue: 'Comma' },
|
||||
period: { i18nKey: 'HotkeyKeys.period', defaultValue: 'Period' },
|
||||
slash: { i18nKey: 'HotkeyKeys.slash', defaultValue: 'Slash' },
|
||||
backslash: { i18nKey: 'HotkeyKeys.backslash', defaultValue: 'Backslash' },
|
||||
semicolon: { i18nKey: 'HotkeyKeys.semicolon', defaultValue: 'Semicolon' },
|
||||
quote: { i18nKey: 'HotkeyKeys.quote', defaultValue: 'Quote' },
|
||||
apostrophe: { i18nKey: 'HotkeyKeys.quote', defaultValue: 'Quote' },
|
||||
backquote: { i18nKey: 'HotkeyKeys.backquote', defaultValue: 'Backtick' },
|
||||
tilde: { i18nKey: 'HotkeyKeys.backquote', defaultValue: 'Backtick' },
|
||||
bracketleft: { i18nKey: 'HotkeyKeys.bracketleft', defaultValue: 'Left Bracket' },
|
||||
bracketright: { i18nKey: 'HotkeyKeys.bracketright', defaultValue: 'Right Bracket' },
|
||||
};
|
||||
|
||||
const formatFallbackToken = (token: string) => {
|
||||
if (!token) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (/^f\d{1,2}$/i.test(token)) {
|
||||
return token.toUpperCase();
|
||||
}
|
||||
|
||||
if (token.length === 1) {
|
||||
return token.toUpperCase();
|
||||
}
|
||||
|
||||
return token.charAt(0).toUpperCase() + token.slice(1);
|
||||
};
|
||||
|
||||
const normalizeHotkeyValue = (value?: string | string[]) => {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.join('+');
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const translateHotkeyValue = (value: string | string[] | undefined, t: TFunction) => {
|
||||
const normalizedValue = normalizeHotkeyValue(value);
|
||||
if (!normalizedValue) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return normalizedValue
|
||||
.split('+')
|
||||
.map(rawToken => {
|
||||
const trimmed = rawToken.trim();
|
||||
if (!trimmed) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const lower = trimmed.toLowerCase();
|
||||
const config = HOTKEY_TOKEN_TRANSLATIONS[lower];
|
||||
if (config) {
|
||||
return t(config.i18nKey, { defaultValue: config.defaultValue });
|
||||
}
|
||||
|
||||
return formatFallbackToken(trimmed);
|
||||
})
|
||||
.join('+');
|
||||
};
|
||||
|
||||
interface UserPreferencesModalProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
@@ -72,6 +178,12 @@ interface HotkeyProps {
|
||||
|
||||
function Hotkey({ label, placeholder, className, value, onChange, hotkeys }: HotkeyProps) {
|
||||
const [isRecording, setIsRecording] = React.useState(false);
|
||||
const { t } = useTranslation('UserPreferencesModal');
|
||||
const translatedValue = React.useMemo(() => translateHotkeyValue(value, t), [value, t]);
|
||||
const translatedPlaceholder = React.useMemo(
|
||||
() => translateHotkeyValue(placeholder, t),
|
||||
[placeholder, t]
|
||||
);
|
||||
|
||||
const onInputKeyDown = (event: React.KeyboardEvent) => {
|
||||
event.preventDefault();
|
||||
@@ -95,15 +207,15 @@ function Hotkey({ label, placeholder, className, value, onChange, hotkeys }: Hot
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('flex items-center justify-between space-x-2', className)}>
|
||||
<Label className="whitespace-nowrap">{label}</Label>
|
||||
<div className={cn('flex items-start justify-between gap-2', className)}>
|
||||
<Label className="flex-1 whitespace-normal break-words text-sm">{label}</Label>
|
||||
<Input
|
||||
className={cn(
|
||||
'w-16 text-center transition-colors',
|
||||
isRecording && 'bg-accent text-accent-foreground caret-accent-foreground'
|
||||
)}
|
||||
placeholder={isRecording ? 'Press keys...' : placeholder}
|
||||
value={value}
|
||||
placeholder={isRecording ? t('Press keys') : translatedPlaceholder || ''}
|
||||
value={isRecording ? normalizeHotkeyValue(value) : translatedValue}
|
||||
onKeyDown={onInputKeyDown}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Button, Icons } from '@ohif/ui-next';
|
||||
import { useSegmentationTableContext, useSegmentationExpanded } from './contexts';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const AddSegmentRow: React.FC<{ children?: React.ReactNode }> = ({ children = null }) => {
|
||||
const { t } = useTranslation('SegmentationPanel');
|
||||
const {
|
||||
activeRepresentation,
|
||||
disableEditing,
|
||||
@@ -56,7 +58,7 @@ export const AddSegmentRow: React.FC<{ children?: React.ReactNode }> = ({ childr
|
||||
onClick={() => onSegmentAdd(segmentationId)}
|
||||
>
|
||||
<Icons.Add />
|
||||
Add Segment
|
||||
{t('Add Segment')}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useSegmentationTableContext } from './contexts';
|
||||
export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
|
||||
children = null,
|
||||
}) => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
const { t } = useTranslation('SegmentationPanel');
|
||||
|
||||
const {
|
||||
onSegmentationAdd,
|
||||
|
||||
@@ -49,7 +49,7 @@ const SegmentationCollapsedDropdownMenu = ({ children }: { children: React.React
|
||||
|
||||
// Selector component - for the segmentation selection dropdown
|
||||
const SegmentationCollapsedSelector = () => {
|
||||
const { t } = useTranslation('SegmentationTable.HeaderCollapsed');
|
||||
const { t } = useTranslation('SegmentationPanel.HeaderCollapsed');
|
||||
const { data, onSegmentationClick, segmentationRepresentationType } = useSegmentationTableContext(
|
||||
'SegmentationCollapsedSelector'
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ interface SegmentationTableComponent extends React.FC<SegmentationTableProps> {
|
||||
}
|
||||
|
||||
export const SegmentationTableRoot = (props: SegmentationTableProps) => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
const { t } = useTranslation('SegmentationPanel');
|
||||
const {
|
||||
data = [],
|
||||
mode,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Input } from '../Input';
|
||||
import { useSegmentationTableContext } from './contexts';
|
||||
|
||||
export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||
const { t } = useTranslation('SegmentationTable.AppearanceSettings');
|
||||
const { t } = useTranslation('SegmentationPanel');
|
||||
const {
|
||||
renderFill,
|
||||
renderOutline,
|
||||
@@ -84,7 +84,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
<div className="space-y-2">
|
||||
<div className="my-2 flex items-center">
|
||||
<Label className="text-muted-foreground w-14 flex-none whitespace-nowrap text-xs">
|
||||
Opacity
|
||||
{t('Opacity')}
|
||||
</Label>
|
||||
<Slider
|
||||
className="mx-1 flex-1"
|
||||
@@ -143,7 +143,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
{renderInactiveSegmentations && (
|
||||
<div className="my-2 flex items-center">
|
||||
<Label className="text-muted-foreground w-14 flex-none whitespace-nowrap text-xs">
|
||||
Opacity
|
||||
{t('Opacity')}
|
||||
</Label>
|
||||
<Slider
|
||||
className="mx-1 flex-1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as utils from './utils';
|
||||
import { cn } from './utils';
|
||||
import { cn, formatDICOMDate } from './utils';
|
||||
export * from './components';
|
||||
export * from './contextProviders';
|
||||
export * as Types from './types';
|
||||
export { utils, cn };
|
||||
export { utils, cn, formatDICOMDate };
|
||||
export { useSessionStorage, useDynamicMaxHeight } from './hooks';
|
||||
@@ -0,0 +1,25 @@
|
||||
import moment from 'moment';
|
||||
import i18n from 'i18next';
|
||||
|
||||
/**
|
||||
* Formats DICOM date.
|
||||
*
|
||||
* @param {string} date
|
||||
* @param {string} strFormat
|
||||
* @returns {string} formatted date.
|
||||
*/
|
||||
export function formatDICOMDate(date: string, strFormat?: string): string {
|
||||
if (!date) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const format = strFormat ?? i18n.t('Common:localDateFormat', 'MMM D, YYYY');
|
||||
const locale = i18n.language || 'en';
|
||||
const parsed = moment(date, ['YYYYMMDD', 'YYYY.MM.DD'], true);
|
||||
|
||||
if (!parsed.isValid()) {
|
||||
return moment(date).locale(locale).format(format);
|
||||
}
|
||||
|
||||
return parsed.locale(locale).format(format);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getToggledClassName } from './getToggledClassName';
|
||||
import roundNumber from './roundNumber';
|
||||
import { cn } from '../lib/utils';
|
||||
import { formatDICOMDate } from './formatDICOMDate';
|
||||
|
||||
export { getToggledClassName, roundNumber, cn };
|
||||
export { getToggledClassName, roundNumber, cn, formatDICOMDate };
|
||||
Reference in new issue
Block a user