Merged PR 225: Implement translations

Implement translations with useContext in React
+Add events to allow changing the language in the app
+Refactor AppEvents
+Redesign vertical bars in elements
This commit is contained in:
Eric Nguyen 2022-11-04 10:58:06 +00:00
parent 60a3ead6aa
commit 505813d530
26 changed files with 527 additions and 160 deletions

View file

@ -1,4 +1,4 @@
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
import React, { Dispatch, SetStateAction, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { UseCustomEvents } from '../../Events/AppEvents';
import { MainMenu } from '../MainMenu/MainMenu';
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
@ -7,6 +7,7 @@ import { IEditorState } from '../../Interfaces/IEditorState';
import { LoadState } from './Actions/Load';
import { LoadEditor, NewEditor } from './Actions/MenuActions';
import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
import { LanguageContext } from '../LanguageProvider/LanguageProvider';
// App will never have props
// eslint-disable-next-line @typescript-eslint/no-empty-interface
@ -44,6 +45,7 @@ function UseHTTPGETStatePreloading(
export function App(props: IAppProps): JSX.Element {
const [isLoaded, setLoaded] = useState<boolean>(false);
const appRef = useRef<HTMLDivElement>(null);
const languageContext = useContext(LanguageContext);
const defaultMainContainer = new ContainerModel(
DEFAULT_MAINCONTAINER_PROPS
@ -68,6 +70,7 @@ export function App(props: IAppProps): JSX.Element {
UseCustomEvents(
props.root,
appRef,
languageContext,
setEditorState,
setLoaded
);

View file

@ -14,6 +14,7 @@ import {
Cog8ToothIcon as Cog8ToothIconS
} from '@heroicons/react/24/solid';
import { BarIcon } from './BarIcon';
import { Text } from '../Text/Text';
interface IBarProps {
isComponentsOpen: boolean
@ -35,7 +36,7 @@ export function Bar(props: IBarProps): JSX.Element {
<div className='bar'>
<BarIcon
isActive={props.isComponentsOpen}
title='Components'
title={Text({ textId: '@Components' })}
onClick={() => props.toggleComponents()}>
{
props.isComponentsOpen
@ -45,7 +46,7 @@ export function Bar(props: IBarProps): JSX.Element {
</BarIcon>
<BarIcon
isActive={props.isSymbolsOpen}
title='Symbols'
title={Text({ textId: '@Symbols' })}
onClick={() => props.toggleSymbols()}>
{
props.isSymbolsOpen
@ -55,7 +56,7 @@ export function Bar(props: IBarProps): JSX.Element {
</BarIcon>
<BarIcon
isActive={props.isMessagesOpen}
title='Messages'
title={Text({ textId: '@Messages' })}
onClick={() => props.toggleMessages()}>
{
props.isMessagesOpen
@ -66,7 +67,7 @@ export function Bar(props: IBarProps): JSX.Element {
<div className='grow'></div>
<BarIcon
isActive={props.isHistoryOpen}
title='Timeline'
title={Text({ textId: '@Timeline' })}
onClick={() => props.toggleTimeline()}>
{
props.isHistoryOpen
@ -76,7 +77,7 @@ export function Bar(props: IBarProps): JSX.Element {
</BarIcon>
<BarIcon
isActive={props.isSettingsOpen}
title='Settings'
title={Text({ textId: '@Settings' })}
onClick={() => props.toggleSettings()}>
{
props.isSettingsOpen

View file

@ -5,6 +5,7 @@ import { ICategory } from '../../Interfaces/ICategory';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { TruncateString } from '../../utils/stringtools';
import { Category } from '../Category/Category';
import { Text } from '../Text/Text';
interface IComponentsProps {
selectedContainer: IContainerModel | undefined
@ -24,7 +25,7 @@ interface SidebarCategory {
export function Components(props: IComponentsProps): JSX.Element {
const [hideDisabled, setHideDisabled] = React.useState<boolean>(false);
const disabledTitle = hideDisabled ? 'Show disabled components' : 'Hide disabled components';
const disabledTitle = hideDisabled ? Text({ textId: '@ShowDisabledComponents' }) : Text({ textId: '@HideDisabledComponents' });
const rootElements: Array<JSX.Element | undefined> = [];
const categories = new Map<string, SidebarCategory>(props.categories.map(category => [

View file

@ -4,6 +4,7 @@ import { IContainerProperties } from '../../Interfaces/IContainerProperties';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../utils/default';
import { ApplyWidthMargin, ApplyXMargin, RemoveWidthMargin, RemoveXMargin, RestoreX, RestoreY, TransformX, TransformY } from '../../utils/svg';
import { Text } from '../Text/Text';
import { InputGroup } from '../InputGroup/InputGroup';
import { TextInputGroup } from '../InputGroup/TextInputGroup';
import { Select } from '../Select/Select';
@ -44,7 +45,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<div className='grid grid-cols-1 gap-y-4 items-center'>
<TextInputGroup
id={`${props.properties.id}-displayedText`}
labelText='Displayed text'
labelText={Text({ textId: '@ContainerDisplayedText' })}
inputKey='displayedText'
labelClassName=''
inputClassName=''
@ -54,7 +55,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<OrientationSelector
id='orientation'
name='Orientation'
labelText='Orientation'
labelText={Text({ textId: '@ContainerOrientation' })}
value={props.properties.orientation}
onChange={props.onChange}
/>
@ -62,14 +63,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<Category
category={{
Type: 'Properties',
DisplayedText: 'Properties'
DisplayedText: Text({ textId: '@ContainerProperties' })
}}
heightClass={`${categoryHeight}`}
>
<div className='grid grid-cols-1 gap-y-6 items-center prop-category-body'>
<div>
<InputGroup
labelText='Name'
labelText={Text({ textId: '@ContainerName' })}
inputKey='id'
labelClassName=''
inputClassName=''
@ -79,7 +80,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
</div>
<div>
<InputGroup
labelText='Parent name'
labelText={Text({ textId: '@ContainerParentName' })}
inputKey='parentId'
labelClassName=''
inputClassName=''
@ -89,7 +90,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
</div>
<div>
<InputGroup
labelText='Type'
labelText={Text({ textId: '@ContainerType' })}
inputKey='type'
labelClassName=''
inputClassName=''
@ -102,7 +103,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<Category category={{
Type: 'Position',
DisplayedText: 'Position'
DisplayedText: Text({ textId: '@ContainerPosition' })
}}
defaultIsOpen={true}
heightClass={`${categoryHeight}`}
@ -110,7 +111,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<div className='grid grid-cols-3 gap-y-2 items-center prop-category-body'>
<TextInputGroup
id={`${props.properties.id}-x`}
labelText='x'
labelText={Text({ textId: '@ContainerX' })}
inputKey='x'
labelClassName=''
inputClassName='col-span-2'
@ -134,7 +135,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
)} />
<TextInputGroup
id={`${props.properties.id}-y`}
labelText='y'
labelText={Text({ textId: '@ContainerY' })}
inputKey='y'
labelClassName=''
inputClassName='col-span-2'
@ -158,16 +159,17 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
</div>
</Category>
<Category category={{
Type: 'Size',
DisplayedText: 'Size'
}}
heightClass={`${categoryHeight}`}
<Category
category={{
Type: 'Size',
DisplayedText: Text({ textId: '@ContainerSize' })
}}
heightClass={`${categoryHeight}`}
>
<div className='grid grid-cols-5 gap-y-2 items-center prop-category-body'>
<TextInputGroup
id={`${props.properties.id}-minWidth`}
labelText='Minimum Width'
labelText={Text({ textId: '@ContainerMinWidth' })}
inputKey='minWidth'
labelClassName='col-span-2'
inputClassName='col-span-3'
@ -177,7 +179,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
onChange={(value) => props.onChange('minWidth', Number(value))} />
<TextInputGroup
id={`${props.properties.id}-width`}
labelText='Width'
labelText={Text({ textId: '@ContainerWidth' })}
inputKey='width'
labelClassName='col-span-2'
inputClassName='col-span-3'
@ -189,7 +191,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
isDisabled={props.properties.isFlex} />
<TextInputGroup
id={`${props.properties.id}-maxWidth`}
labelText='Maximum Width'
labelText={Text({ textId: '@ContainerMaxWidth' })}
inputKey='maxWidth'
labelClassName='col-span-2'
inputClassName='col-span-3'
@ -200,7 +202,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<div className='col-span-5 p-3'></div>
<TextInputGroup
id={`${props.properties.id}-minHeight`}
labelText='Minimum Height'
labelText={Text({ textId: '@ContainerMinHeight' })}
inputKey='minHeight'
labelClassName='col-span-2'
inputClassName='col-span-3'
@ -210,7 +212,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
onChange={(value) => props.onChange('minHeight', Number(value))} />
<TextInputGroup
id={`${props.properties.id}-height`}
labelText='Height'
labelText={Text({ textId: '@ContainerHeight' })}
inputKey='height'
labelClassName='col-span-2'
inputClassName='col-span-3'
@ -223,7 +225,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
/>
<TextInputGroup
id={`${props.properties.id}-maxHeight`}
labelText='Maximum Height'
labelText={Text({ textId: '@ContainerMaxHeight' })}
inputKey='maxHeight'
labelClassName='col-span-2'
inputClassName='col-span-3'
@ -236,14 +238,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<Category category={{
Type: 'Margins',
DisplayedText: 'Margins'
DisplayedText: Text({ textId: '@ContainerMargins' })
}}
heightClass={`${categoryHeight}`}
>
<div className='grid grid-cols-2 items-center gap-y-2 prop-category-body'>
<TextInputGroup
id={`${props.properties.id}-ml`}
labelText='Margin left'
labelText={Text({ textId: '@ContainerMarginLeft' })}
inputKey='left'
labelClassName=''
inputClassName=''
@ -253,7 +255,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
onChange={(value) => props.onChange('left', Number(value), PropertyType.Margin)} />
<TextInputGroup
id={`${props.properties.id}-mb`}
labelText='Margin bottom'
labelText={Text({ textId: '@ContainerMarginBottom' })}
inputKey='bottom'
labelClassName=''
inputClassName=''
@ -263,7 +265,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
onChange={(value) => props.onChange('bottom', Number(value), PropertyType.Margin)} />
<TextInputGroup
id={`${props.properties.id}-mt`}
labelText='Margin top'
labelText={Text({ textId: '@ContainerMarginTop' })}
inputKey='top'
labelClassName=''
inputClassName=''
@ -273,7 +275,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
onChange={(value) => props.onChange('top', Number(value), PropertyType.Margin)} />
<TextInputGroup
id={`${props.properties.id}-mr`}
labelText='Margin right'
labelText={Text({ textId: '@ContainerMarginRight' })}
inputKey='right'
labelClassName=''
inputClassName=''
@ -286,13 +288,13 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<Category category={{
Type: 'Behaviors',
DisplayedText: 'Behaviors'
DisplayedText: Text({ textId: '@ContainerBehaviors' })
}}
heightClass={`${categoryHeight}`}
>
<div className='grid grid-cols-2 items-center gap-y-2 prop-category-body'>
<ToggleButton
labelText='Flex'
labelText={Text({ textId: '@ContainerFlex' })}
inputKey='isFlex'
labelClassName=''
inputClassName='ml-auto mr-auto block'
@ -301,7 +303,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
onChange={(event) => props.onChange('isFlex', event.target.checked)}
/>
<ToggleButton
labelText='Anchor'
labelText={Text({ textId: '@ContainerAnchor' })}
inputKey='isAnchor'
labelClassName=''
inputClassName='ml-auto mr-auto block'
@ -313,7 +315,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<Category category={{
Type: 'Alignment',
DisplayedText: 'Alignment'
DisplayedText: Text({ textId: '@ContainerAlignment' })
}}
heightClass={`${categoryHeight}`}
>
@ -321,14 +323,14 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<PositionReferenceSelector
id='positionReference'
name='PositionReference'
labelText='Alignment'
labelText={Text({ textId: '@ContainerAlignmentInput' })}
value={props.properties.positionReference}
onChange={props.onChange}
/>
<div className='p-3'></div>
<Select
inputKey='linkedSymbolId'
labelText='Align with symbol'
labelText={Text({ textId: '@ContainerAlignWithSymbol' })}
labelClassName=''
inputClassName=''
inputs={[...props.symbols.values()].map(symbol => ({
@ -343,7 +345,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<Category category={{
Type: 'Dimensions',
DisplayedText: 'Dimensions'
DisplayedText: Text({ textId: '@ContainerDimensions' })
}}
heightClass={`${categoryHeight}`}
>
@ -354,7 +356,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<PositionCheckboxes
id='showSelfDimensions'
name='ShowSelfDimensions'
labelText='Show dimension'
labelText={Text({ textId: '@ContainerShowDimension' })}
value={props.properties.showSelfDimensions}
onChange={props.onChange}
/>
@ -366,7 +368,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
<PositionCheckboxes
id='showChildrenDimensions'
name='ShowChildrenDimensions'
labelText='Show overall dimension of its children'
labelText={Text({ textId: '@ContainerShowChildrenDimension' })}
value={props.properties.showChildrenDimensions}
onChange={props.onChange}
/>
@ -374,33 +376,33 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
}
{
SHOW_BORROWER_DIMENSIONS &&
<>
<div className='grid grid-cols-1 gap-2'>
<OrientationCheckboxes
id='markPosition'
name='MarkPosition'
value={props.properties.markPosition}
labelText='Mark the position'
onChange={props.onChange}
/>
</div>
<div className='grid grid-cols-1 gap-2'>
<PositionCheckboxes
id='showDimensionWithMarks'
name='ShowDimensionWithMarks'
labelText='Show dimension with marked children'
value={props.properties.showDimensionWithMarks}
onChange={props.onChange}
/>
</div>
</>
<>
<div className='grid grid-cols-1 gap-2'>
<OrientationCheckboxes
id='markPosition'
name='MarkPosition'
value={props.properties.markPosition}
labelText={Text({ textId: '@ContainerMarkPosition' })}
onChange={props.onChange}
/>
</div>
<div className='grid grid-cols-1 gap-2'>
<PositionCheckboxes
id='showDimensionWithMarks'
name='ShowDimensionWithMarks'
labelText={Text({ textId: '@ContainerShowDimensionWithMarks' })}
value={props.properties.showDimensionWithMarks}
onChange={props.onChange}
/>
</div>
</>
}
</div>
</Category>
<Category category={{
Type: 'Style',
DisplayedText: 'Style'
DisplayedText: Text({ textId: '@ContainerStyle' })
}}
heightClass={`${categoryHeight}`}
>

View file

@ -15,6 +15,7 @@ import { GetCurrentHistoryState } from '../Editor';
import { AddContainers } from './AddContainer';
import { DeleteContainer } from './ContainerOperations';
import { DeleteSymbol } from './SymbolOperations';
import { Text } from '../../Text/Text';
export function InitActions(
menuActions: Map<string, IMenuAction[]>,
@ -28,8 +29,8 @@ export function InitActions(
'',
[
{
text: 'Undo',
title: 'Undo last action',
text: Text({ textId: '@Undo' }),
title: Text({ textId: '@UndoTitle' }),
shortcut: '<kbd>Ctrl</kbd>+<kbd>Z</kbd>',
action: () => {
if (historyCurrentStep <= 0) {
@ -39,8 +40,8 @@ export function InitActions(
}
},
{
text: 'Redo',
title: 'Redo last action',
text: Text({ textId: '@Redo' }),
title: Text({ textId: '@RedoTitle' }),
shortcut: '<kbd>Ctrl</kbd>+<kbd>Y</kbd>',
action: () => {
if (historyCurrentStep >= history.length - 1) {
@ -55,8 +56,8 @@ export function InitActions(
menuActions.set(
'elements-sidebar-row',
[{
text: 'Delete',
title: 'Delete the container',
text: Text({ textId: '@DeleteContainer' }),
title: Text({ textId: '@DeleteContainerTitle' }),
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;
@ -73,8 +74,8 @@ export function InitActions(
menuActions.set(
'symbols-sidebar-row',
[{
text: 'Delete',
title: 'Delete the container',
text: Text({ textId: '@DeleteSymbol' }),
title: Text({ textId: '@DeleteSymbolTitle' }),
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;

View file

@ -135,7 +135,6 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
}): JSX.Element {
const { container, depth } = containers[index];
const key = container.properties.id.toString();
const tabs = '|\t'.repeat(depth);
const text = container.properties.displayedText === key
? `${key}`
: `${container.properties.displayedText}`;
@ -144,29 +143,19 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
props.selectedContainer !== null &&
props.selectedContainer.properties.id === container.properties.id;
const selectedClass: string = isSelected
? 'border-l-4 bg-blue-500 shadow-lg shadow-blue-500/60 hover:bg-blue-600 hover:shadow-blue-500 text-slate-50'
: 'bg-slate-300/60 hover:bg-slate-400 hover:shadow-slate-400';
return (
<button type="button"
className={`transition-all w-full border-blue-500 hover:shadow-lg elements-sidebar-row whitespace-pre
text-left text-sm font-medium inline-flex ${container.properties.type} ${selectedClass}`}
id={key}
key={key}
style={style}
title={container.properties.warning}
onClick={() => props.selectContainer(container.properties.id)}
onDrop={(event) => HandleOnDrop(event, props.containers, props.mainContainer, props.addContainer)}
onDragOver={(event) => HandleDragOver(event, props.mainContainer)}
onDragLeave={(event) => HandleDragLeave(event)}
>
{tabs}
{text}
{container.properties.warning.length > 0 &&
<ExclamationTriangleIcon className='w-8'/>
}
</button>
ElementsListRow(
key,
container,
depth,
isSelected,
style,
text,
props.containers,
props.mainContainer,
props.addContainer,
props.selectContainer
)
);
}
@ -193,3 +182,52 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
</div>
);
}
function ElementsListRow(
key: string,
container: IContainerModel,
depth: number,
isSelected: boolean,
style: React.CSSProperties,
text: string,
containers: Map<string, IContainerModel>,
mainContainer: IContainerModel,
addContainer: (index: number, type: string, parent: string) => void,
selectContainer: (containerId: string) => void
): JSX.Element {
const verticalBars: JSX.Element[] = [];
const verticalBarSelectedClass = isSelected
? 'border-l-blue-400 group-hover:border-l-blue-300'
: 'border-l-slate-400 group-hover:border-l-slate-300';
for (let i = 0; i < depth; i++) {
verticalBars.push(
<span
className={`h-full border-l-2 pr-2 ${verticalBarSelectedClass}`}
></span>
);
}
const buttonSelectedClass: string = isSelected
? 'bg-blue-500 shadow-lg shadow-blue-500/60 hover:bg-blue-600 hover:shadow-blue-500 text-slate-50'
: 'bg-slate-300/60 hover:bg-slate-400 hover:shadow-slate-400';
return <button type="button"
className={`transition-all border-blue-500 hover:shadow-lg elements-sidebar-row whitespace-pre
text-left text-sm font-medium inline-flex group ${container.properties.type} ${buttonSelectedClass}`}
id={key}
key={key}
style={style}
title={container.properties.warning}
onClick={() => selectContainer(container.properties.id)}
onDrop={(event) => HandleOnDrop(event, containers, mainContainer, addContainer)}
onDragOver={(event) => HandleDragOver(event, mainContainer)}
onDragLeave={(event) => HandleDragLeave(event)}
>
{verticalBars}
<div className='pt-2 pb-2 inline-flex'>
{text}
{container.properties.warning.length > 0 &&
<ExclamationTriangleIcon className='pl-2 w-7' />}
</div>
</button>;
}

View file

@ -0,0 +1,31 @@
import * as React from 'react';
import { createContext, useState } from 'react';
import { ILanguage } from '../../Interfaces/ILanguage';
import { languageOptions, translations } from '../../Translations/Translations';
import { DEFAULT_LANGUAGE } from '../../utils/default';
// eslint-disable-next-line @typescript-eslint/naming-convention
export const LanguageContext = createContext<ILanguage>({
language: DEFAULT_LANGUAGE,
dictionary: translations.en
});
export function LanguageProvider({ children }: { children: React.ReactNode | React.ReactNode[] | undefined }): JSX.Element {
const [language, setLanguage] = useState(DEFAULT_LANGUAGE);
const provider = {
language,
dictionary: translations[language],
languageChange: (selected: string) => {
const newLanguage = languageOptions[selected] !== undefined ? selected : DEFAULT_LANGUAGE;
setLanguage(newLanguage);
}
};
return (
<LanguageContext.Provider
value={provider}
>
{ children }
</LanguageContext.Provider>
);
};

View file

@ -1,6 +1,7 @@
import * as React from 'react';
import { FAST_BOOT } from '../../utils/default';
import { Loader } from '../Loader/Loader';
import { Text } from '../Text/Text';
interface IMainMenuProps {
newEditor: () => void
@ -82,8 +83,16 @@ export function MainMenu(props: IMainMenuProps): JSX.Element {
<button type="button" className='mainmenu-btn' onClick={() => {
setWindowState(WindowState.Loading);
props.newEditor();
}}>Start from scratch</button>
<button type="button" className='mainmenu-btn' onClick={() => setWindowState(WindowState.Load)}>Load a configuration file</button>
}}>
{Text({ textId: '@StartFromScratch' })}
</button>
<button
type="button"
className='mainmenu-btn'
onClick={() => setWindowState(WindowState.Load)}
>
{Text({ textId: '@LoadConfigFile' })}
</button>
</div>
);
}

View file

@ -7,6 +7,7 @@ import { IMessage } from '../../Interfaces/IMessage';
import { DISABLE_API } from '../../utils/default';
import { GetCircularReplacer } from '../../utils/saveload';
import { TITLE_BAR_HEIGHT } from '../Sidebar/Sidebar';
import { Text } from '../Text/Text';
interface IMessagesProps {
historyState: IHistoryState
@ -49,8 +50,8 @@ export function Messages(props: IMessagesProps): JSX.Element {
<button
onClick={() => { props.clearMessage(); }}
className='h-full hover:bg-slate-400 rounded-lg p-1'
aria-label='Clear all messages'
title='Clear all messages'
aria-label={Text({ textId: '@ClearAllMessages' })}
title={Text({ textId: '@ClearAllMessages' })}
>
<TrashIcon className='heroicon'></TrashIcon>
</button>

View file

@ -1,5 +1,6 @@
import { ArrowUpOnSquareIcon, CameraIcon } from '@heroicons/react/24/outline';
import React from 'react';
import { Text } from '../Text/Text';
interface ISettingsProps {
saveEditorAsJSON: () => void
@ -12,19 +13,19 @@ export function Settings(props: ISettingsProps): JSX.Element {
m-2 md:text-xs font-bold'>
<button type="button"
className={'w-full transition-all flex sidebar-component'}
title='Export as JSON'
title={Text({ textId: '@ExportAsJSON' })}
onClick={props.saveEditorAsJSON}
>
<ArrowUpOnSquareIcon className="heroicon w-16 h-7" />
Export as JSON
{Text({ textId: '@ExportAsJSON' })}
</button>
<button type="button"
className={'w-full transition-all flex sidebar-component'}
title='Export as SVG'
title={Text({ textId: '@ExportAsSVG' })}
onClick={props.saveEditorAsSVG}
>
<CameraIcon className="heroicon w-16 h-7" />
Export as SVG
{Text({ textId: '@ExportAsSVG' })}
</button>
</div>
);

View file

@ -4,6 +4,7 @@ import { RestoreX, TransformX } from '../../utils/svg';
import { InputGroup } from '../InputGroup/InputGroup';
import { TextInputGroup } from '../InputGroup/TextInputGroup';
import { PositionReferenceSelector } from '../RadioGroupButtons/PositionReferenceSelector';
import { Text } from '../Text/Text';
interface ISymbolFormProps {
symbol: ISymbolModel
@ -15,7 +16,7 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
return (
<div className='grid grid-cols-2 gap-y-4'>
<InputGroup
labelText='Name'
labelText={Text({ textId: '@SymbolName' })}
inputKey='id'
labelClassName=''
inputClassName=''
@ -24,7 +25,7 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
isDisabled={true} />
<TextInputGroup
id='x'
labelText='x'
labelText={Text({ textId: '@SymbolX' })}
inputKey='x'
labelClassName=''
inputClassName=''
@ -33,7 +34,7 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
onChange={(value) => props.onChange('x', RestoreX(Number(value), props.symbol.width, props.symbol.config.PositionReference))} />
<TextInputGroup
id='height'
labelText='Height'
labelText={Text({ textId: '@SymbolHeight' })}
inputKey='height'
labelClassName=''
inputClassName=''
@ -43,7 +44,7 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
onChange={(value) => props.onChange('height', Number(value))} />
<TextInputGroup
id='width'
labelText='Width'
labelText={Text({ textId: '@SymbolWidth' })}
inputKey='width'
labelClassName=''
inputClassName=''

View file

@ -0,0 +1,11 @@
import { useContext } from 'react';
import { LanguageContext } from '../LanguageProvider/LanguageProvider';
interface TextProps {
textId: string
};
export function Text({ textId }: TextProps): string {
const languageContext = useContext(LanguageContext);
return languageContext.dictionary[textId] ?? textId;
};

View file

@ -16,6 +16,7 @@ import { UseWorker, UseAsync } from './UseWorker';
import { FindContainerById } from '../../utils/itertools';
import { IEditorState } from '../../Interfaces/IEditorState';
import { GetCurrentHistoryState } from '../Editor/Editor';
import { Text } from '../Text/Text';
export interface IUIProps {
editorState: IEditorState
@ -95,14 +96,14 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
switch (selectedSidebar) {
case SidebarType.Components:
leftSidebarTitle = 'Components';
leftSidebarTitle = Text({ textId: '@Components' });
leftChildren = <Components
selectedContainer={selectedContainer}
componentOptions={configuration.AvailableContainers}
categories={configuration.Categories}
buttonOnClick={methods.addContainer}
/>;
rightSidebarTitle = 'Elements';
rightSidebarTitle = Text({ textId: '@Elements' });
rightChildren = <ElementsList
containers={current.containers}
mainContainer={mainContainer}
@ -115,12 +116,12 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
break;
case SidebarType.Symbols:
leftSidebarTitle = 'Symbols';
leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
leftChildren = <Symbols
componentOptions={configuration.AvailableSymbols}
buttonOnClick={methods.addSymbol}
/>;
rightSidebarTitle = 'Symbols';
rightSidebarTitle = Text({ textId: '@SymbolsRight' });
rightChildren = <SymbolsSidebar
selectedSymbolId={current.selectedSymbolId}
symbols={current.symbols}
@ -130,7 +131,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
break;
case SidebarType.History:
leftSidebarTitle = 'Timeline';
leftSidebarTitle = Text({ textId: '@Timeline' });
leftChildren = <History
history={editorState.history}
historyCurrentStep={editorState.historyCurrentStep}
@ -139,7 +140,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
break;
case SidebarType.Messages:
leftSidebarTitle = 'Messages';
leftSidebarTitle = Text({ textId: '@Messages' });
leftChildren = <Messages
historyState={current}
messages={messages}
@ -148,7 +149,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
break;
case SidebarType.Settings:
leftSidebarTitle = 'Settings';
leftSidebarTitle = Text({ textId: '@Settings' });
leftChildren = <Settings
saveEditorAsJSON={methods.saveEditorAsJSON}
saveEditorAsSVG={methods.saveEditorAsSVG}