Floor editor updates

This commit is contained in:
Bill 2022-03-06 01:08:54 -05:00
parent 04e6d3eeea
commit fed35dd1f0
12 changed files with 350 additions and 303 deletions

View File

@ -83,6 +83,9 @@ $nitro-doorbell-height: 200px;
$nitro-guide-tool-width: 250px;
$nitro-floor-editor-width: 760px;
$nitro-floor-editor-height: 500px;
.nitro-app {
width: 100%;
height: 100%;

View File

@ -0,0 +1,25 @@
import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react';
import { IFloorplanSettings } from './common/IFloorplanSettings';
import { IVisualizationSettings } from './common/IVisualizationSettings';
interface IFloorplanEditorContext
{
originalFloorplanSettings: IFloorplanSettings;
setOriginalFloorplanSettings: Dispatch<SetStateAction<IFloorplanSettings>>;
visualizationSettings: IVisualizationSettings;
setVisualizationSettings: Dispatch<SetStateAction<IVisualizationSettings>>;
}
const FloorplanEditorContext = createContext<IFloorplanEditorContext>({
originalFloorplanSettings: null,
setOriginalFloorplanSettings: null,
visualizationSettings: null,
setVisualizationSettings: null
});
export const FloorplanEditorContextProvider: FC<ProviderProps<IFloorplanEditorContext>> = props =>
{
return <FloorplanEditorContext.Provider value={ props.value }>{ props.children }</FloorplanEditorContext.Provider>
}
export const useFloorplanEditorContext = () => useContext(FloorplanEditorContext);

View File

@ -1,20 +1,6 @@
.nitro-floorplan-editor {
width: 760px;
height: 575px;
.editor-area {
width: 100%;
height: 300px;
min-height: 300px;
overflow: scroll;
}
.arrow-button {
@include media-breakpoint-up(md) {
display:none;
}
}
width: $nitro-floor-editor-width;
height: $nitro-floor-editor-height;
}
.floorplan-import-export {

View File

@ -1,29 +1,37 @@
import { FloorHeightMapEvent, NitroPoint, RoomEngineEvent, RoomVisualizationSettingsEvent, UpdateFloorPropertiesMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { FC, useCallback, useEffect, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../api';
import { Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common';
import { Button, ButtonGroup, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common';
import { FloorplanEditorEvent } from '../../events';
import { UseMessageEventHook, UseMountEffect, UseRoomEngineEvent, UseUiEvent } from '../../hooks';
import { UseMessageEventHook, UseRoomEngineEvent, UseUiEvent } from '../../hooks';
import { FloorplanEditor } from './common/FloorplanEditor';
import { IFloorplanSettings } from './common/IFloorplanSettings';
import { IVisualizationSettings } from './common/IVisualizationSettings';
import { convertNumbersForSaving, convertSettingToNumber } from './common/Utils';
import { FloorplanEditorContextProvider } from './context/FloorplanEditorContext';
import { IFloorplanSettings, initialFloorplanSettings, IVisualizationSettings } from './context/FloorplanEditorContext.types';
import { FloorplanEditorContextProvider } from './FloorplanEditorContext';
import { FloorplanCanvasView } from './views/FloorplanCanvasView';
import { FloorplanImportExportView } from './views/FloorplanImportExportView';
import { FloorplanOptionsView } from './views/FloorplanOptionsView';
export const FloorplanEditorView: FC<{}> = props =>
{
const [isVisible, setIsVisible] = useState(false);
const [ isVisible, setIsVisible ] = useState(false);
const [ importExportVisible, setImportExportVisible ] = useState(false);
const [originalFloorplanSettings, setOriginalFloorplanSettings] = useState<IFloorplanSettings>(initialFloorplanSettings);
const [visualizationSettings, setVisualizationSettings] = useState<IVisualizationSettings>(
{
entryPointDir: 2,
wallHeight: -1,
thicknessWall: 1,
thicknessFloor: 1
});
const [ originalFloorplanSettings, setOriginalFloorplanSettings ] = useState<IFloorplanSettings>({
tilemap: '',
reservedTiles: [],
entryPoint: [0, 0],
entryPointDir: 2,
wallHeight: -1,
thicknessWall: 1,
thicknessFloor: 1
});
const [ visualizationSettings, setVisualizationSettings ] = useState<IVisualizationSettings>({
entryPointDir: 2,
wallHeight: -1,
thicknessWall: 1,
thicknessFloor: 1
});
const onFloorplanEditorEvent = useCallback((event: FloorplanEditorEvent) =>
{
@ -36,20 +44,15 @@ export const FloorplanEditorView: FC<{}> = props =>
setIsVisible(true);
break;
case FloorplanEditorEvent.TOGGLE_FLOORPLAN_EDITOR:
setIsVisible(!isVisible);
setIsVisible(prevValue => !prevValue);
break;
}
}, [isVisible]);
}, []);
UseUiEvent(FloorplanEditorEvent.HIDE_FLOORPLAN_EDITOR, onFloorplanEditorEvent);
UseUiEvent(FloorplanEditorEvent.SHOW_FLOORPLAN_EDITOR, onFloorplanEditorEvent);
UseUiEvent(FloorplanEditorEvent.TOGGLE_FLOORPLAN_EDITOR, onFloorplanEditorEvent);
UseMountEffect(() =>
{
FloorplanEditor.instance.initialize();
});
const onRoomEngineEvent = useCallback((event: RoomEngineEvent) =>
{
setIsVisible(false);
@ -61,18 +64,25 @@ export const FloorplanEditorView: FC<{}> = props =>
{
const parser = event.getParser();
if(!parser) return;
setOriginalFloorplanSettings(prevValue =>
{
const newValue = { ...prevValue };
const settings = Object.assign({}, originalFloorplanSettings);
settings.tilemap = parser.model;
settings.wallHeight = parser.wallHeight + 1;
setOriginalFloorplanSettings(settings);
newValue.tilemap = parser.model;
newValue.wallHeight = (parser.wallHeight + 1);
const vSettings = Object.assign({}, visualizationSettings);
vSettings.wallHeight = parser.wallHeight + 1;
setVisualizationSettings(vSettings);
return newValue;
});
}, [originalFloorplanSettings, visualizationSettings]);
setVisualizationSettings(prevValue =>
{
const newValue = { ...prevValue };
newValue.wallHeight = (parser.wallHeight + 1);
return newValue;
});
}, []);
UseMessageEventHook(FloorHeightMapEvent, onFloorHeightMapEvent);
@ -80,23 +90,30 @@ export const FloorplanEditorView: FC<{}> = props =>
{
const parser = event.getParser();
if(!parser) return;
setOriginalFloorplanSettings(prevValue =>
{
const newValue = { ...prevValue };
const settings = Object.assign({}, originalFloorplanSettings);
settings.thicknessFloor = convertSettingToNumber(parser.thicknessFloor);
settings.thicknessWall = convertSettingToNumber(parser.thicknessWall);
newValue.thicknessFloor = convertSettingToNumber(parser.thicknessFloor);
newValue.thicknessWall = convertSettingToNumber(parser.thicknessWall);
setOriginalFloorplanSettings(settings);
return newValue;
});
const vSettings = Object.assign({}, visualizationSettings);
vSettings.thicknessFloor = convertSettingToNumber(parser.thicknessFloor);
vSettings.thicknessWall = convertSettingToNumber(parser.thicknessWall);
setVisualizationSettings(vSettings);
}, [originalFloorplanSettings, visualizationSettings]);
setVisualizationSettings(prevValue =>
{
const newValue = { ...prevValue };
newValue.thicknessFloor = convertSettingToNumber(parser.thicknessFloor);
newValue.thicknessWall = convertSettingToNumber(parser.thicknessWall);
return newValue;
});
}, []);
UseMessageEventHook(RoomVisualizationSettingsEvent, onRoomVisualizationSettingsEvent);
const saveFloorChanges = useCallback(() =>
const saveFloorChanges = () =>
{
SendMessageComposer(new UpdateFloorPropertiesMessageComposer(
FloorplanEditor.instance.getCurrentTilemapString(),
@ -105,47 +122,44 @@ export const FloorplanEditorView: FC<{}> = props =>
visualizationSettings.entryPointDir,
convertNumbersForSaving(visualizationSettings.thicknessWall),
convertNumbersForSaving(visualizationSettings.thicknessFloor),
visualizationSettings.wallHeight - 1
(visualizationSettings.wallHeight - 1)
));
}, [visualizationSettings.entryPointDir, visualizationSettings.thicknessFloor, visualizationSettings.thicknessWall, visualizationSettings.wallHeight]);
}
const revertChanges = useCallback(() =>
const revertChanges = () =>
{
setVisualizationSettings({ wallHeight: originalFloorplanSettings.wallHeight, thicknessWall: originalFloorplanSettings.thicknessWall, thicknessFloor: originalFloorplanSettings.thicknessFloor, entryPointDir: originalFloorplanSettings.entryPointDir });
FloorplanEditor.instance.doorLocation = new NitroPoint(originalFloorplanSettings.entryPoint[0], originalFloorplanSettings.entryPoint[1]);
FloorplanEditor.instance.setTilemap(originalFloorplanSettings.tilemap, originalFloorplanSettings.reservedTiles);
FloorplanEditor.instance.renderTiles();
}, [originalFloorplanSettings.entryPoint, originalFloorplanSettings.entryPointDir, originalFloorplanSettings.reservedTiles, originalFloorplanSettings.thicknessFloor, originalFloorplanSettings.thicknessWall, originalFloorplanSettings.tilemap, originalFloorplanSettings.wallHeight])
}
useEffect(() =>
{
FloorplanEditor.instance.initialize();
}, []);
return (
<>
<FloorplanEditorContextProvider value={{ originalFloorplanSettings: originalFloorplanSettings, setOriginalFloorplanSettings: setOriginalFloorplanSettings, visualizationSettings: visualizationSettings, setVisualizationSettings: setVisualizationSettings }}>
{isVisible &&
<NitroCardView uniqueKey="floorpan-editor" className="nitro-floorplan-editor" theme="primary-slim">
<NitroCardHeaderView headerText={LocalizeText('floor.plan.editor.title')} onCloseClick={() => setIsVisible(false)} />
<NitroCardContentView>
<Grid>
<Column size={12}>
<FloorplanOptionsView />
<FloorplanCanvasView />
<Flex className="justify-content-between">
<div className="btn-group">
<button className="btn btn-primary" onClick={revertChanges}>{LocalizeText('floor.plan.editor.reload')}</button>
</div>
<div className="btn-group">
<button className="btn btn-primary" disabled={true}>{LocalizeText('floor.plan.editor.preview')}</button>
<button className="btn btn-primary" onClick={ () => setImportExportVisible(true) }>{LocalizeText('floor.plan.editor.import.export')}</button>
<button className="btn btn-primary" onClick={saveFloorChanges}>{LocalizeText('floor.plan.editor.save')}</button>
</div>
</Flex>
</Column>
</Grid>
</NitroCardContentView>
</NitroCardView>
}
{importExportVisible && <FloorplanImportExportView onCloseClick={ () => setImportExportVisible(false)}/>}
</FloorplanEditorContextProvider>
</>
<FloorplanEditorContextProvider value={{ originalFloorplanSettings: originalFloorplanSettings, setOriginalFloorplanSettings: setOriginalFloorplanSettings, visualizationSettings: visualizationSettings, setVisualizationSettings: setVisualizationSettings }}>
{ isVisible &&
<NitroCardView uniqueKey="floorpan-editor" className="nitro-floorplan-editor" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('floor.plan.editor.title') } onCloseClick={ () => setIsVisible(false) } />
<NitroCardContentView overflow="hidden">
<FloorplanOptionsView />
<FloorplanCanvasView overflow="hidden" />
<Flex justifyContent="between">
<Button onClick={ revertChanges }>{ LocalizeText('floor.plan.editor.reload') }</Button>
<ButtonGroup>
<Button disabled={ true }>{ LocalizeText('floor.plan.editor.preview') }</Button>
<Button onClick={ event => setImportExportVisible(true) }>{ LocalizeText('floor.plan.editor.import.export') }</Button>
<Button onClick={ saveFloorChanges }>{ LocalizeText('floor.plan.editor.save') }</Button>
</ButtonGroup>
</Flex>
</NitroCardContentView>
</NitroCardView> }
{ importExportVisible &&
<FloorplanImportExportView onCloseClick={ () => setImportExportVisible(false) } /> }
</FloorplanEditorContextProvider>
);
}

View File

@ -0,0 +1 @@
export const ConvertTileMapToString = (map: string) => map.replace(/\r\n|\r|\n/g, '\n').toLowerCase();

View File

@ -0,0 +1,8 @@
import { IVisualizationSettings } from './IVisualizationSettings';
export interface IFloorplanSettings extends IVisualizationSettings
{
tilemap: string;
reservedTiles: boolean[][];
entryPoint: [ number, number ];
}

View File

@ -0,0 +1,7 @@
export interface IVisualizationSettings
{
entryPointDir: number;
wallHeight: number;
thicknessWall: number;
thicknessFloor: number;
}

View File

@ -1,16 +0,0 @@
import { createContext, FC, useContext } from 'react';
import { FloorplanEditorContextProps, IFloorplanEditorContext } from './FloorplanEditorContext.types';
const FloorplanEditorContext = createContext<IFloorplanEditorContext>({
originalFloorplanSettings: null,
setOriginalFloorplanSettings: null,
visualizationSettings: null,
setVisualizationSettings: null
});
export const FloorplanEditorContextProvider: FC<FloorplanEditorContextProps> = props =>
{
return <FloorplanEditorContext.Provider value={ props.value }>{ props.children }</FloorplanEditorContext.Provider>
}
export const useFloorplanEditorContext = () => useContext(FloorplanEditorContext);

View File

@ -1,37 +0,0 @@
import { ProviderProps } from 'react';
export interface IFloorplanEditorContext
{
originalFloorplanSettings: IFloorplanSettings;
setOriginalFloorplanSettings: React.Dispatch<React.SetStateAction<IFloorplanSettings>>;
visualizationSettings: IVisualizationSettings;
setVisualizationSettings: React.Dispatch<React.SetStateAction<IVisualizationSettings>>;
}
export interface IFloorplanSettings extends IVisualizationSettings {
tilemap: string;
reservedTiles: boolean[][];
entryPoint: [number, number];
}
export interface IVisualizationSettings {
entryPointDir: number;
wallHeight: number;
thicknessWall: number;
thicknessFloor: number;
}
export const initialFloorplanSettings: IFloorplanSettings = {
tilemap: '',
reservedTiles: [],
entryPoint: [0, 0],
entryPointDir: 2,
wallHeight: -1,
thicknessWall: 1,
thicknessFloor: 1
}
export interface FloorplanEditorContextProps extends ProviderProps<IFloorplanEditorContext>
{
}

View File

@ -1,51 +1,49 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { GetOccupiedTilesMessageComposer, GetRoomEntryTileMessageComposer, NitroPoint, RoomEntryTileMessageEvent, RoomOccupiedTilesMessageEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { SendMessageComposer } from '../../../api';
import { Flex } from '../../../common';
import { UseMessageEventHook, UseMountEffect } from '../../../hooks';
import { Base, Button, Column, ColumnProps, Flex, Grid } from '../../../common';
import { UseMessageEventHook } from '../../../hooks';
import { FloorplanEditor } from '../common/FloorplanEditor';
import { useFloorplanEditorContext } from '../context/FloorplanEditorContext';
import { IFloorplanSettings } from '../common/IFloorplanSettings';
import { useFloorplanEditorContext } from '../FloorplanEditorContext';
export const FloorplanCanvasView: FC<{}> = props =>
interface FloorplanCanvasViewProps extends ColumnProps
{
const { originalFloorplanSettings = null, setOriginalFloorplanSettings = null, visualizationSettings = null, setVisualizationSettings = null } = useFloorplanEditorContext();
}
export const FloorplanCanvasView: FC<FloorplanCanvasViewProps> = props =>
{
const { gap = 1, children = null, ...rest } = props;
const [ occupiedTilesReceived , setOccupiedTilesReceived ] = useState(false);
const [ entryTileReceived, setEntryTileReceived ] = useState(false);
const { originalFloorplanSettings = null, setOriginalFloorplanSettings = null, setVisualizationSettings = null } = useFloorplanEditorContext();
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() =>
{
return ( () =>
{
FloorplanEditor.instance.clear();
setVisualizationSettings( prev => {return { wallHeight: originalFloorplanSettings.wallHeight, thicknessWall: originalFloorplanSettings.thicknessWall, thicknessFloor: originalFloorplanSettings.thicknessFloor, entryPointDir: prev.entryPointDir } });
});
}, [originalFloorplanSettings.thicknessFloor, originalFloorplanSettings.thicknessWall, originalFloorplanSettings.wallHeight, setVisualizationSettings]);
UseMountEffect(() =>
{
SendMessageComposer(new GetRoomEntryTileMessageComposer());
SendMessageComposer(new GetOccupiedTilesMessageComposer());
FloorplanEditor.instance.tilemapRenderer.interactive = true;
elementRef.current.appendChild(FloorplanEditor.instance.renderer.view);
});
const onRoomOccupiedTilesMessageEvent = useCallback((event: RoomOccupiedTilesMessageEvent) =>
{
const parser = event.getParser();
if(!parser) return;
let newFloorPlanSettings: IFloorplanSettings = null;
const settings = Object.assign({}, originalFloorplanSettings);
settings.reservedTiles = parser.blockedTilesMap;
setOriginalFloorplanSettings(settings);
setOriginalFloorplanSettings(prevValue =>
{
const newValue = { ...prevValue };
FloorplanEditor.instance.setTilemap(originalFloorplanSettings.tilemap, parser.blockedTilesMap);
newValue.reservedTiles = parser.blockedTilesMap;
newFloorPlanSettings = newValue;
return newValue;
});
FloorplanEditor.instance.setTilemap(newFloorPlanSettings.tilemap, parser.blockedTilesMap);
setOccupiedTilesReceived(true);
elementRef.current.scrollTo(FloorplanEditor.instance.view.width / 3, 0);
}, [originalFloorplanSettings, setOriginalFloorplanSettings]);
elementRef.current.scrollTo((FloorplanEditor.instance.view.width / 3), 0);
}, [ setOriginalFloorplanSettings ]);
UseMessageEventHook(RoomOccupiedTilesMessageEvent, onRoomOccupiedTilesMessageEvent);
@ -53,24 +51,33 @@ export const FloorplanCanvasView: FC<{}> = props =>
{
const parser = event.getParser();
if(!parser) return;
setOriginalFloorplanSettings(prevValue =>
{
const newValue = { ...prevValue };
const settings = Object.assign({}, originalFloorplanSettings);
settings.entryPoint = [parser.x, parser.y];
settings.entryPointDir = parser.direction;
setOriginalFloorplanSettings(settings);
newValue.entryPoint = [ parser.x, parser.y ];
newValue.entryPointDir = parser.direction;
const vSettings = Object.assign({}, visualizationSettings);
vSettings.entryPointDir = parser.direction;
setVisualizationSettings(vSettings);
return newValue;
});
setVisualizationSettings(prevValue =>
{
const newValue = { ...prevValue };
newValue.entryPointDir = parser.direction;
return newValue;
});
FloorplanEditor.instance.doorLocation = new NitroPoint(parser.x, parser.y);
setEntryTileReceived(true);
}, [originalFloorplanSettings, setOriginalFloorplanSettings, setVisualizationSettings, visualizationSettings]);
}, [ setOriginalFloorplanSettings, setVisualizationSettings ]);
UseMessageEventHook(RoomEntryTileMessageEvent, onRoomEntryTileMessageEvent);
const onClickArrowButton = useCallback((scrollDirection: string) =>
const onClickArrowButton = (scrollDirection: string) =>
{
const element = elementRef.current;
@ -91,29 +98,92 @@ export const FloorplanCanvasView: FC<{}> = props =>
element.scrollBy({ left: 10 });
break;
}
}, []);
}
useEffect(() =>
{
if(entryTileReceived && occupiedTilesReceived)
FloorplanEditor.instance.renderTiles();
}, [entryTileReceived, occupiedTilesReceived])
return () =>
{
FloorplanEditor.instance.clear();
setVisualizationSettings(prevValue =>
{
return {
wallHeight: originalFloorplanSettings.wallHeight,
thicknessWall: originalFloorplanSettings.thicknessWall,
thicknessFloor: originalFloorplanSettings.thicknessFloor,
entryPointDir: prevValue.entryPointDir
}
});
}
}, [ originalFloorplanSettings.thicknessFloor, originalFloorplanSettings.thicknessWall, originalFloorplanSettings.wallHeight, setVisualizationSettings ]);
useEffect(() =>
{
if(!entryTileReceived || !occupiedTilesReceived) return;
FloorplanEditor.instance.renderTiles();
}, [ entryTileReceived, occupiedTilesReceived ]);
useEffect(() =>
{
SendMessageComposer(new GetRoomEntryTileMessageComposer());
SendMessageComposer(new GetOccupiedTilesMessageComposer());
FloorplanEditor.instance.tilemapRenderer.interactive = true;
if(!elementRef.current) return;
elementRef.current.appendChild(FloorplanEditor.instance.renderer.view);
}, []);
return (
<>
<Flex className="align-items-center justify-content-center">
<div className="arrow-button"><button className="btn btn-primary" onClick={() => onClickArrowButton('up')}><i className="fas fa-arrow-up"/></button></div>
</Flex>
<Flex className="align-items-center justify-content-center">
<div className="arrow-button"><button className="btn btn-primary" onClick={() => onClickArrowButton('left')}><i className="fas fa-arrow-left"/></button></div>
<div className="rounded-2 overflow-hidden">
<div ref={elementRef} className="editor-area" />
</div>
<div className="arrow-button"><button className="btn btn-primary" onClick={() => onClickArrowButton('right')}><i className="fas fa-arrow-right"/></button></div>
</Flex>
<Flex className="align-items-center justify-content-center">
<div className="arrow-button"><button className="btn btn-primary" onClick={() => onClickArrowButton('down')}><i className="fas fa-arrow-down"/></button></div>
</Flex>
</>
<Column gap={ gap } { ...rest }>
<Grid overflow="hidden" gap={ 1 }>
<Column center size={ 1 }>
<Button className="d-md-none" onClick={ event => onClickArrowButton('left') }>
<FontAwesomeIcon icon="arrow-left" />
</Button>
</Column>
<Column overflow="hidden" size={ 10 } gap={ 1 }>
<Flex justifyContent="center" className="d-md-none">
<Button shrink onClick={ event => onClickArrowButton('up') }>
<FontAwesomeIcon icon="arrow-up" />
</Button>
</Flex>
<Base overflow="auto" innerRef={ elementRef } />
<Flex justifyContent="center" className="d-md-none">
<Button shrink onClick={ event => onClickArrowButton('down') }>
<FontAwesomeIcon icon="arrow-down" />
</Button>
</Flex>
</Column>
<Column center size={ 1 }>
<Button className="d-md-none" onClick={ event => onClickArrowButton('right') }>
<FontAwesomeIcon icon="arrow-right" />
</Button>
</Column>
</Grid>
{/* <Flex center className="d-md-none">
<Button onClick={ event => onClickArrowButton('up') }>
<FontAwesomeIcon icon="arrow-up" />
</Button>
</Flex>
<Flex center gap={ 1 }>
<Button className="d-md-none" onClick={ event => onClickArrowButton('left') }>
<FontAwesomeIcon icon="arrow-left" />
</Button>
<Base overflow="auto" innerRef={ elementRef } />
<Button className="d-md-none" onClick={ event => onClickArrowButton('right') }>
<FontAwesomeIcon icon="arrow-right" />
</Button>
</Flex>
<Flex center className="d-md-none">
<Button onClick={ event => onClickArrowButton('down') }>
<FontAwesomeIcon icon="arrow-down" />
</Button>
</Flex> */}
{ children }
</Column>
);
}

View File

@ -1,29 +1,24 @@
import { UpdateFloorPropertiesMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { FC, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../api';
import { Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../common';
import { Button, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../common';
import { UseMountEffect } from '../../../hooks';
import { ConvertTileMapToString } from '../common/ConvertMapToString';
import { convertNumbersForSaving } from '../common/Utils';
import { useFloorplanEditorContext } from '../context/FloorplanEditorContext';
import { useFloorplanEditorContext } from '../FloorplanEditorContext';
interface FloorplanImportExportViewProps
{
onCloseClick(): void;
}
export const FloorplanImportExportView: FC<FloorplanImportExportViewProps> = props =>
{
const { originalFloorplanSettings = null, setOriginalFloorplanSettings = null } = useFloorplanEditorContext();
const { onCloseClick = null } = props;
const [ map, setMap ] = useState<string>('');
const { originalFloorplanSettings = null } = useFloorplanEditorContext();
const convertMapToString = useCallback((map: string) =>
{
return map.replace(/\r\n|\r|\n/g, '\n').toLowerCase();
}, []);
const revertChanges= useCallback(() =>
{
setMap(convertMapToString(originalFloorplanSettings.tilemap));
}, [convertMapToString, originalFloorplanSettings.tilemap]);
const saveFloorChanges = useCallback(() =>
const saveFloorChanges = () =>
{
SendMessageComposer(new UpdateFloorPropertiesMessageComposer(
map.split('\n').join('\r'),
@ -34,35 +29,27 @@ export const FloorplanImportExportView: FC<FloorplanImportExportViewProps> = pro
convertNumbersForSaving(originalFloorplanSettings.thicknessFloor),
originalFloorplanSettings.wallHeight - 1
));
}, [map, originalFloorplanSettings.entryPoint, originalFloorplanSettings.entryPointDir, originalFloorplanSettings.thicknessFloor, originalFloorplanSettings.thicknessWall, originalFloorplanSettings.wallHeight]);
}
UseMountEffect(() =>
{
revertChanges();
setMap(ConvertTileMapToString(originalFloorplanSettings.tilemap));
});
return (
<NitroCardView theme="primary-slim" className="floorplan-import-export">
<NitroCardHeaderView headerText={LocalizeText('floor.plan.editor.import.export')} onCloseClick={ onCloseClick } />
<NitroCardHeaderView headerText={ LocalizeText('floor.plan.editor.import.export') } onCloseClick={ onCloseClick } />
<NitroCardContentView>
<Column size={ 12 } className="h-100">
<textarea className="h-100" value={map} onChange={ event => setMap(event.target.value) }></textarea>
<Flex className="justify-content-between">
<div className="btn-group">
<button className="btn btn-primary" onClick={revertChanges}>{LocalizeText('floor.plan.editor.revert.to.last.received.map')}</button>
</div>
<div className="btn-group">
<button className="btn btn-primary" onClick={saveFloorChanges}>{LocalizeText('floor.plan.editor.save')}</button>
</div>
</Flex>
</Column>
<textarea className="h-100" value={ map } onChange={ event => setMap(event.target.value) } />
<Flex justifyContent="between">
<Button onClick={ event => setMap(ConvertTileMapToString(originalFloorplanSettings.tilemap))}>
{ LocalizeText('floor.plan.editor.revert.to.last.received.map') }
</Button>
<Button onClick={ saveFloorChanges }>
{ LocalizeText('floor.plan.editor.save') }
</Button>
</Flex>
</NitroCardContentView>
</NitroCardView>
)
}
export interface FloorplanImportExportViewProps
{
onCloseClick(): void;
);
}

View File

@ -1,10 +1,11 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC, useCallback, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../api';
import { Base, Column, Flex, Grid, LayoutGridItem } from '../../../common';
import { Column, Flex, LayoutGridItem, Text } from '../../../common';
import { COLORMAP, FloorAction } from '../common/Constants';
import { FloorplanEditor } from '../common/FloorplanEditor';
import { useFloorplanEditorContext } from '../context/FloorplanEditorContext';
import { useFloorplanEditorContext } from '../FloorplanEditorContext';
const MIN_WALL_HEIGHT: number = 0;
const MAX_WALL_HEIGHT: number = 16;
@ -111,77 +112,75 @@ export const FloorplanOptionsView: FC<{}> = props =>
}
return (
<Grid className="h-auto">
<Column size={ 5 }>
<Column gap={ 2 } overflow="hidden">
<Base className="flex-shrink-0 fw-bold text-black text-truncate">{ LocalizeText('floor.plan.editor.draw.mode') }</Base>
<Grid>
<LayoutGridItem itemActive={ (floorAction === FloorAction.SET) } onClick={ event => selectAction(FloorAction.SET) }>
<i className="icon icon-set-tile" />
</LayoutGridItem>
<LayoutGridItem itemActive={ (floorAction === FloorAction.UNSET) } onClick={ event => selectAction(FloorAction.UNSET) }>
<i className="icon icon-unset-tile" />
</LayoutGridItem>
<LayoutGridItem itemActive={ (floorAction === FloorAction.UP) } onClick={ event => selectAction(FloorAction.UP) }>
<i className="icon icon-increase-height" />
</LayoutGridItem>
<LayoutGridItem itemActive={ (floorAction === FloorAction.DOWN) } onClick={ event => selectAction(FloorAction.DOWN) }>
<i className="icon icon-decrease-height" />
</LayoutGridItem>
<Column>
<Flex gap={ 1 }>
<Column size={ 5 } gap={ 1 }>
<Text bold>{ LocalizeText('floor.plan.editor.draw.mode') }</Text>
<Flex gap={ 3 }>
<Flex gap={ 1 }>
<LayoutGridItem itemActive={ (floorAction === FloorAction.SET) } onClick={ event => selectAction(FloorAction.SET) }>
<i className="icon icon-set-tile" />
</LayoutGridItem>
<LayoutGridItem itemActive={ (floorAction === FloorAction.UNSET) } onClick={ event => selectAction(FloorAction.UNSET) }>
<i className="icon icon-unset-tile" />
</LayoutGridItem>
</Flex>
<Flex gap={ 1 }>
<LayoutGridItem itemActive={ (floorAction === FloorAction.UP) } onClick={ event => selectAction(FloorAction.UP) }>
<i className="icon icon-increase-height" />
</LayoutGridItem>
<LayoutGridItem itemActive={ (floorAction === FloorAction.DOWN) } onClick={ event => selectAction(FloorAction.DOWN) }>
<i className="icon icon-decrease-height" />
</LayoutGridItem>
</Flex>
<LayoutGridItem itemActive={ (floorAction === FloorAction.DOOR) } onClick={ event => selectAction(FloorAction.DOOR) }>
<i className="icon icon-set-door" />
</LayoutGridItem>
</Grid>
</Column>
</Column>
<Column className="align-items-center overflow-hidden" size={ 4 }>
<Column gap={ 2 } overflow="hidden">
<Base className="flex-shrink-0 fw-bold text-black text-truncate">{ LocalizeText('floor.plan.editor.enter.direction') }</Base>
<i className={ `icon icon-door-direction-${ visualizationSettings.entryPointDir } cursor-pointer` } onClick={ changeDoorDirection } />
</Column>
</Column>
<Column className="align-items-center" size={ 3 }>
<Column gap={ 2 } overflow="hidden">
<Base className="flex-shrink-0 fw-bold text-black text-truncate">{ LocalizeText('floor.editor.wall.height') }</Base>
<Flex className="align-items-center">
<i className="fas fa-caret-left cursor-pointer me-1 text-black" onClick={ decreaseWallHeight } />
<input type="number" className="form-control form-control-sm quantity-input" value={ visualizationSettings.wallHeight } onChange={ event => onWallHeightChange(event.target.valueAsNumber)} />
<i className="fas fa-caret-right cursor-pointer ms-1 text-black" onClick={ increaseWallHeight } />
</Flex>
</Column>
</Column>
<Column size={ 5 }>
<Column gap={ 2 } overflow="hidden">
<Base className="flex-shrink-0 fw-bold text-black text-truncate">{ LocalizeText('floor.plan.editor.tile.height') }: { floorHeight }</Base>
<Column alignItems="center" size={ 4 }>
<Text bold>{ LocalizeText('floor.plan.editor.enter.direction') }</Text>
<i className={ `icon icon-door-direction-${ visualizationSettings.entryPointDir } cursor-pointer` } onClick={ changeDoorDirection } />
</Column>
<Column size={ 3 }>
<Text bold>{ LocalizeText('floor.editor.wall.height') }</Text>
<Flex alignItems="center" gap={ 1 }>
<FontAwesomeIcon icon="caret-left" className="cursor-pointer" onClick={ decreaseWallHeight } />
<input type="number" className="form-control form-control-sm quantity-input" value={ visualizationSettings.wallHeight } onChange={ event => onWallHeightChange(event.target.valueAsNumber)} />
<FontAwesomeIcon icon="caret-right" className="cursor-pointer" onClick={ increaseWallHeight } />
</Flex>
</Column>
</Flex>
<Flex gap={ 1 }>
<Column size={ 6 }>
<Text bold>{ LocalizeText('floor.plan.editor.tile.height') }: { floorHeight }</Text>
<ReactSlider
className="nitro-slider"
min={ MIN_FLOOR_HEIGHT }
max={ MAX_FLOOR_HEIGHT }
step={ 1 }
value={ floorHeight }
onChange={ event => onFloorHeightChange(event) }
renderThumb={ ({ style, ...rest }, state) => <div style={ { backgroundColor: `#${COLORMAP[state.valueNow.toString(33)]}`, ...style } } { ...rest }>{ state.valueNow }</div> } />
className="nitro-slider"
min={ MIN_FLOOR_HEIGHT }
max={ MAX_FLOOR_HEIGHT }
step={ 1 }
value={ floorHeight }
onChange={ event => onFloorHeightChange(event) }
renderThumb={ ({ style, ...rest }, state) => <div style={ { backgroundColor: `#${COLORMAP[state.valueNow.toString(33)]}`, ...style } } { ...rest }>{ state.valueNow }</div> } />
</Column>
</Column>
<Column size={5}>
<Column gap={ 2 } overflow="hidden">
<Base className="flex-shrink-0 fw-bold text-black text-truncate">{ LocalizeText('floor.plan.editor.room.options') }</Base>
<Flex className="align-items-center">
<select className="form-control form-control-sm" value={visualizationSettings.thicknessWall} onChange={event => onWallThicknessChange(parseInt(event.target.value))}>
<option value={0}>{ LocalizeText('navigator.roomsettings.wall_thickness.thinnest') }</option>
<option value={1}>{ LocalizeText('navigator.roomsettings.wall_thickness.thin') }</option>
<option value={2}>{ LocalizeText('navigator.roomsettings.wall_thickness.normal') }</option>
<option value={3}>{ LocalizeText('navigator.roomsettings.wall_thickness.thick') }</option>
</select>
<select className="form-control form-control-sm" value={visualizationSettings.thicknessFloor} onChange={event => onFloorThicknessChange(parseInt(event.target.value))}>
<option value={0}>{ LocalizeText('navigator.roomsettings.floor_thickness.thinnest') }</option>
<option value={1}>{ LocalizeText('navigator.roomsettings.floor_thickness.thin') }</option>
<option value={2}>{ LocalizeText('navigator.roomsettings.floor_thickness.normal') }</option>
<option value={3}>{ LocalizeText('navigator.roomsettings.floor_thickness.thick') }</option>
</select>
</Flex>
<Column size={ 6 }>
<Text bold>{ LocalizeText('floor.plan.editor.room.options') }</Text>
<Flex className="align-items-center">
<select className="form-control form-control-sm" value={ visualizationSettings.thicknessWall } onChange={ event => onWallThicknessChange(parseInt(event.target.value)) }>
<option value={ 0 }>{ LocalizeText('navigator.roomsettings.wall_thickness.thinnest') }</option>
<option value={ 1 }>{ LocalizeText('navigator.roomsettings.wall_thickness.thin') }</option>
<option value={ 2 }>{ LocalizeText('navigator.roomsettings.wall_thickness.normal') }</option>
<option value={ 3 }>{ LocalizeText('navigator.roomsettings.wall_thickness.thick') }</option>
</select>
<select className="form-control form-control-sm" value={ visualizationSettings.thicknessFloor } onChange={ event => onFloorThicknessChange(parseInt(event.target.value)) }>
<option value={ 0 }>{ LocalizeText('navigator.roomsettings.floor_thickness.thinnest') }</option>
<option value={ 1 }>{ LocalizeText('navigator.roomsettings.floor_thickness.thin') }</option>
<option value={ 2 }>{ LocalizeText('navigator.roomsettings.floor_thickness.normal') }</option>
<option value={ 3 }>{ LocalizeText('navigator.roomsettings.floor_thickness.thick') }</option>
</select>
</Flex>
</Column>
</Column>
</Grid>
</Flex>
</Column>
);
}