Add option for the properties form to only update on submit #23
12 changed files with 256 additions and 53 deletions
|
@ -1,4 +1,4 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import React, { Dispatch, SetStateAction } from 'react';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||||
import { Configuration } from '../../Interfaces/Configuration';
|
import { Configuration } from '../../Interfaces/Configuration';
|
||||||
import { ContainerModel, IContainerModel } from '../../Interfaces/ContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
|
@ -60,7 +60,7 @@ export function DeleteContainer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
if (container === null || container === undefined) {
|
||||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
throw new Error('[DeleteContainer] Container model was not found among children of the main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.parent != null) {
|
if (container.parent != null) {
|
||||||
|
@ -266,6 +266,76 @@ export function OnPropertyChange(
|
||||||
setHistoryCurrentStep(history.length);
|
setHistoryCurrentStep(history.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handled the property change event in the properties form
|
||||||
|
* @param key Property name
|
||||||
|
* @param value New value of the property
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
export function OnPropertiesSubmit(
|
||||||
|
event: React.SyntheticEvent<HTMLFormElement>,
|
||||||
|
refs: Array<React.RefObject<HTMLInputElement>>,
|
||||||
|
fullHistory: HistoryState[],
|
||||||
|
historyCurrentStep: number,
|
||||||
|
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||||
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
|
): void {
|
||||||
|
event.preventDefault();
|
||||||
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
|
if (current.SelectedContainer === null ||
|
||||||
|
current.SelectedContainer === undefined) {
|
||||||
|
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent === null) {
|
||||||
|
const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer);
|
||||||
|
for (const ref of refs) {
|
||||||
|
const input = ref.current;
|
||||||
|
if (input instanceof HTMLInputElement) {
|
||||||
|
(selectedContainerClone.properties as any)[input.id] = input.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setHistory(history.concat([{
|
||||||
|
LastAction: 'Change property of main',
|
||||||
|
MainContainer: selectedContainerClone,
|
||||||
|
SelectedContainer: selectedContainerClone,
|
||||||
|
SelectedContainerId: selectedContainerClone.properties.id,
|
||||||
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
|
}]));
|
||||||
|
setHistoryCurrentStep(history.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||||
|
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
||||||
|
|
||||||
|
if (container === null || container === undefined) {
|
||||||
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ref of refs) {
|
||||||
|
const input = ref.current;
|
||||||
|
if (input instanceof HTMLInputElement) {
|
||||||
|
(container.properties as any)[input.id] = input.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container.properties.isRigidBody) {
|
||||||
|
RecalculatePhysics(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
setHistory(history.concat([{
|
||||||
|
LastAction: `Change property of container ${container.properties.id}`,
|
||||||
|
MainContainer: mainContainerClone,
|
||||||
|
SelectedContainer: container,
|
||||||
|
SelectedContainerId: container.properties.id,
|
||||||
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
|
}]));
|
||||||
|
setHistoryCurrentStep(history.length);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO put this in a different file
|
// TODO put this in a different file
|
||||||
|
|
||||||
export function RecalculatePhysics(container: IContainerModel): IContainerModel {
|
export function RecalculatePhysics(container: IContainerModel): IContainerModel {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Configuration } from '../../Interfaces/Configuration';
|
||||||
import { SVG } from '../SVG/SVG';
|
import { SVG } from '../SVG/SVG';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||||
import { UI } from '../UI/UI';
|
import { UI } from '../UI/UI';
|
||||||
import { SelectContainer, DeleteContainer, OnPropertyChange, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
import { SelectContainer, DeleteContainer, OnPropertyChange, AddContainerToSelectedContainer, AddContainer, OnPropertiesSubmit } from './ContainerOperations';
|
||||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
||||||
import { onKeyDown } from './Shortcuts';
|
import { onKeyDown } from './Shortcuts';
|
||||||
|
|
||||||
|
@ -72,6 +72,14 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
setHistory,
|
setHistory,
|
||||||
setHistoryCurrentStep
|
setHistoryCurrentStep
|
||||||
)}
|
)}
|
||||||
|
OnPropertiesSubmit={(event, refs) => OnPropertiesSubmit(
|
||||||
|
event,
|
||||||
|
refs,
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
setHistory,
|
||||||
|
setHistoryCurrentStep
|
||||||
|
)}
|
||||||
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
||||||
type,
|
type,
|
||||||
configuration,
|
configuration,
|
||||||
|
|
|
@ -25,6 +25,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={null}
|
SelectedContainer={null}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
|
OnPropertiesSubmit={() => {}}
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -57,6 +58,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={MainContainer}
|
SelectedContainer={MainContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
|
OnPropertiesSubmit={() => {}}
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -70,12 +72,12 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
expect(screen.queryByText('y')).toBeDefined();
|
expect(screen.queryByText('y')).toBeDefined();
|
||||||
expect(screen.queryByText('width')).toBeDefined();
|
expect(screen.queryByText('width')).toBeDefined();
|
||||||
expect(screen.queryByText('height')).toBeDefined();
|
expect(screen.queryByText('height')).toBeDefined();
|
||||||
const propertyId = container.querySelector('#property-id');
|
const propertyId = container.querySelector('#id');
|
||||||
const propertyParentId = container.querySelector('#property-parentId');
|
const propertyParentId = container.querySelector('#parentId');
|
||||||
const propertyX = container.querySelector('#property-x');
|
const propertyX = container.querySelector('#x');
|
||||||
const propertyY = container.querySelector('#property-y');
|
const propertyY = container.querySelector('#y');
|
||||||
const propertyWidth = container.querySelector('#property-width');
|
const propertyWidth = container.querySelector('#width');
|
||||||
const propertyHeight = container.querySelector('#property-height');
|
const propertyHeight = container.querySelector('#height');
|
||||||
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
||||||
expect(propertyParentId).toBeDefined();
|
expect(propertyParentId).toBeDefined();
|
||||||
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
||||||
|
@ -146,6 +148,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={MainContainer}
|
SelectedContainer={MainContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
|
OnPropertiesSubmit={() => {}}
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -202,6 +205,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={SelectedContainer}
|
SelectedContainer={SelectedContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
|
OnPropertiesSubmit={() => {}}
|
||||||
SelectContainer={selectContainer}
|
SelectContainer={selectContainer}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -212,8 +216,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
expect(screen.getByText(/main/i));
|
expect(screen.getByText(/main/i));
|
||||||
const child1 = screen.getByText(/child-1/i);
|
const child1 = screen.getByText(/child-1/i);
|
||||||
expect(child1);
|
expect(child1);
|
||||||
const propertyId = container.querySelector('#property-id');
|
const propertyId = container.querySelector('#id');
|
||||||
const propertyParentId = container.querySelector('#property-parentId');
|
const propertyParentId = container.querySelector('#parentId');
|
||||||
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
||||||
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
||||||
|
|
||||||
|
@ -225,6 +229,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={SelectedContainer}
|
SelectedContainer={SelectedContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
|
OnPropertiesSubmit={() => {}}
|
||||||
SelectContainer={selectContainer}
|
SelectContainer={selectContainer}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
|
|
@ -14,6 +14,7 @@ interface IElementsSidebarProps {
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
SelectedContainer: IContainerModel | null
|
SelectedContainer: IContainerModel | null
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
|
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, refs: Array<React.RefObject<HTMLInputElement>>) => void
|
||||||
SelectContainer: (container: IContainerModel) => void
|
SelectContainer: (container: IContainerModel) => void
|
||||||
DeleteContainer: (containerid: string) => void
|
DeleteContainer: (containerid: string) => void
|
||||||
AddContainer: (index: number, type: string, parent: string) => void
|
AddContainer: (index: number, type: string, parent: string) => void
|
||||||
|
@ -145,7 +146,11 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
props.DeleteContainer(onClickContainerId);
|
props.DeleteContainer(onClickContainerId);
|
||||||
}} />
|
}} />
|
||||||
</Menu>
|
</Menu>
|
||||||
<Properties properties={props.SelectedContainer?.properties} onChange={props.OnPropertyChange}></Properties>
|
<Properties
|
||||||
|
properties={props.SelectedContainer?.properties}
|
||||||
|
onChange={props.OnPropertyChange}
|
||||||
|
onSubmit={props.OnPropertiesSubmit}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,13 +38,8 @@ export const MainMenu: React.FC<IMainMenuProps> = (props) => {
|
||||||
</form>
|
</form>
|
||||||
<button
|
<button
|
||||||
onClick={() => setWindowState(WindowState.MAIN)}
|
onClick={() => setWindowState(WindowState.MAIN)}
|
||||||
className='block text-sm
|
className='normal-btn block
|
||||||
mt-8 py-2 px-4
|
mt-8 '
|
||||||
rounded-full border-0
|
|
||||||
font-semibold
|
|
||||||
transition-all
|
|
||||||
bg-blue-100 text-blue-700
|
|
||||||
hover:bg-blue-200'
|
|
||||||
>
|
>
|
||||||
Go back
|
Go back
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,6 +8,7 @@ describe.concurrent('Properties', () => {
|
||||||
render(<Properties
|
render(<Properties
|
||||||
properties={undefined}
|
properties={undefined}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
|
onSubmit={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.queryByText('id')).toBeNull();
|
expect(screen.queryByText('id')).toBeNull();
|
||||||
|
@ -16,7 +17,7 @@ describe.concurrent('Properties', () => {
|
||||||
expect(screen.queryByText('y')).toBeNull();
|
expect(screen.queryByText('y')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Some properties', () => {
|
it('Some properties, change values with dynamic input', () => {
|
||||||
const prop = {
|
const prop = {
|
||||||
id: 'stuff',
|
id: 'stuff',
|
||||||
parentId: 'parentId',
|
parentId: 'parentId',
|
||||||
|
@ -32,6 +33,7 @@ describe.concurrent('Properties', () => {
|
||||||
const { container, rerender } = render(<Properties
|
const { container, rerender } = render(<Properties
|
||||||
properties={prop}
|
properties={prop}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onSubmit={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.queryByText('id')).toBeDefined();
|
expect(screen.queryByText('id')).toBeDefined();
|
||||||
|
@ -39,10 +41,10 @@ describe.concurrent('Properties', () => {
|
||||||
expect(screen.queryByText('x')).toBeDefined();
|
expect(screen.queryByText('x')).toBeDefined();
|
||||||
expect(screen.queryByText('y')).toBeDefined();
|
expect(screen.queryByText('y')).toBeDefined();
|
||||||
|
|
||||||
let propertyId = container.querySelector('#property-id');
|
let propertyId = container.querySelector('#id');
|
||||||
let propertyParentId = container.querySelector('#property-parentId');
|
let propertyParentId = container.querySelector('#parentId');
|
||||||
let propertyX = container.querySelector('#property-x');
|
let propertyX = container.querySelector('#x');
|
||||||
let propertyY = container.querySelector('#property-y');
|
let propertyY = container.querySelector('#y');
|
||||||
expect(propertyId).toBeDefined();
|
expect(propertyId).toBeDefined();
|
||||||
expect((propertyId as HTMLInputElement).value).toBe('stuff');
|
expect((propertyId as HTMLInputElement).value).toBe('stuff');
|
||||||
expect(propertyParentId).toBeDefined();
|
expect(propertyParentId).toBeDefined();
|
||||||
|
@ -65,12 +67,13 @@ describe.concurrent('Properties', () => {
|
||||||
rerender(<Properties
|
rerender(<Properties
|
||||||
properties={Object.assign({}, prop)}
|
properties={Object.assign({}, prop)}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onSubmit={() => {}}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
propertyId = container.querySelector('#property-id');
|
propertyId = container.querySelector('#id');
|
||||||
propertyParentId = container.querySelector('#property-parentId');
|
propertyParentId = container.querySelector('#parentId');
|
||||||
propertyX = container.querySelector('#property-x');
|
propertyX = container.querySelector('#x');
|
||||||
propertyY = container.querySelector('#property-y');
|
propertyY = container.querySelector('#y');
|
||||||
expect(propertyId).toBeDefined();
|
expect(propertyId).toBeDefined();
|
||||||
expect((propertyId as HTMLInputElement).value).toBe('stuffed');
|
expect((propertyId as HTMLInputElement).value).toBe('stuffed');
|
||||||
expect(propertyParentId).toBeDefined();
|
expect(propertyParentId).toBeDefined();
|
||||||
|
|
|
@ -1,25 +1,50 @@
|
||||||
import * as React from 'react';
|
import React, { useState } from 'react';
|
||||||
import ContainerProperties from '../../Interfaces/Properties';
|
import ContainerProperties from '../../Interfaces/Properties';
|
||||||
|
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
||||||
import { INPUT_TYPES } from './PropertiesInputTypes';
|
import { INPUT_TYPES } from './PropertiesInputTypes';
|
||||||
|
|
||||||
interface IPropertiesProps {
|
interface IPropertiesProps {
|
||||||
properties?: ContainerProperties
|
properties?: ContainerProperties
|
||||||
onChange: (key: string, value: string | number | boolean) => void
|
onChange: (key: string, value: string | number | boolean) => void
|
||||||
|
onSubmit: (event: React.FormEvent<HTMLFormElement>, refs: Array<React.RefObject<HTMLInputElement>>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps) => {
|
export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps) => {
|
||||||
|
const [isDynamicInput, setIsDynamicInput] = useState<boolean>(true);
|
||||||
|
|
||||||
if (props.properties === undefined) {
|
if (props.properties === undefined) {
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupInput: React.ReactNode[] = [];
|
const groupInput: React.ReactNode[] = [];
|
||||||
|
const refs: Array<React.RefObject<HTMLInputElement>> = [];
|
||||||
Object
|
Object
|
||||||
.entries(props.properties)
|
.entries(props.properties)
|
||||||
.forEach((pair) => handleProperties(pair, groupInput, props.onChange));
|
.forEach((pair) => handleProperties(pair, groupInput, refs, isDynamicInput, props.onChange));
|
||||||
|
|
||||||
|
const form = isDynamicInput
|
||||||
|
? <div>
|
||||||
|
{ groupInput }
|
||||||
|
</div>
|
||||||
|
: <form
|
||||||
|
key={props.properties.id}
|
||||||
|
onSubmit={(event) => props.onSubmit(event, refs)}
|
||||||
|
>
|
||||||
|
<input type='submit' className='normal-btn block mx-auto' value='Submit'/>
|
||||||
|
{ groupInput }
|
||||||
|
</form>
|
||||||
|
;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-3 bg-slate-200 h-3/5 overflow-y-auto'>
|
<div className='h-3/5 p-3 bg-slate-200 overflow-y-auto'>
|
||||||
{ groupInput }
|
<ToggleButton
|
||||||
|
id='isDynamic'
|
||||||
|
text='Dynamic update'
|
||||||
|
title='Enable dynamic svg update'
|
||||||
|
checked={isDynamicInput}
|
||||||
|
onChange={() => setIsDynamicInput(!isDynamicInput)}
|
||||||
|
/>
|
||||||
|
{ form }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -27,6 +52,8 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
|
||||||
const handleProperties = (
|
const handleProperties = (
|
||||||
[key, value]: [string, string | number],
|
[key, value]: [string, string | number],
|
||||||
groupInput: React.ReactNode[],
|
groupInput: React.ReactNode[],
|
||||||
|
refs: Array<React.RefObject<HTMLInputElement>>,
|
||||||
|
isDynamicInput: boolean,
|
||||||
onChange: (key: string, value: string | number | boolean) => void
|
onChange: (key: string, value: string | number | boolean) => void
|
||||||
): void => {
|
): void => {
|
||||||
const id = `property-${key}`;
|
const id = `property-${key}`;
|
||||||
|
@ -42,31 +69,49 @@ const handleProperties = (
|
||||||
type = INPUT_TYPES[key];
|
type = INPUT_TYPES[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDisabled = ['id', 'parentId'].includes(key);
|
const ref: React.RefObject<HTMLInputElement> = React.useRef<HTMLInputElement>(null);
|
||||||
///
|
refs.push(ref);
|
||||||
|
|
||||||
groupInput.push(
|
const isDisabled = ['id', 'parentId'].includes(key);
|
||||||
<div key={id} className='mt-4'>
|
const input = isDynamicInput
|
||||||
<label className='text-sm font-medium text-gray-800' htmlFor={id}>{key}</label>
|
? <input
|
||||||
<input
|
className='text-base font-medium transition-all text-gray-800 mt-1 block w-full px-3 py-2
|
||||||
className='text-base font-medium transition-all text-gray-800 mt-1 block w-full px-3 py-2
|
|
||||||
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
||||||
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
||||||
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none
|
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none
|
||||||
'
|
'
|
||||||
type={type}
|
type={type}
|
||||||
id={id}
|
id={key}
|
||||||
value={value}
|
ref={ref}
|
||||||
checked={checked}
|
value={value}
|
||||||
onChange={(event) => {
|
checked={checked}
|
||||||
if (type === 'checkbox') {
|
onChange={(event) => {
|
||||||
onChange(key, event.target.checked);
|
if (type === 'checkbox') {
|
||||||
return;
|
onChange(key, event.target.checked);
|
||||||
}
|
return;
|
||||||
onChange(key, event.target.value);
|
}
|
||||||
}}
|
onChange(key, event.target.value);
|
||||||
disabled={isDisabled}
|
}}
|
||||||
/>
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
: <input
|
||||||
|
className='text-base font-medium transition-all text-gray-800 mt-1 block w-full px-3 py-2
|
||||||
|
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
||||||
|
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
||||||
|
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none
|
||||||
|
'
|
||||||
|
type={type}
|
||||||
|
id={key}
|
||||||
|
ref={ref}
|
||||||
|
defaultValue={value}
|
||||||
|
defaultChecked={checked}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
groupInput.push(
|
||||||
|
<div key={id} className='mt-4'>
|
||||||
|
<label className='text-sm font-medium text-gray-800' htmlFor={key}>{key}</label>
|
||||||
|
{input}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
8
src/Components/ToggleButton/ToggleButton.scss
Normal file
8
src/Components/ToggleButton/ToggleButton.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
input:checked ~ .dot {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
input:checked ~ .line {
|
||||||
|
background-color: #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
52
src/Components/ToggleButton/ToggleButton.tsx
Normal file
52
src/Components/ToggleButton/ToggleButton.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import './ToggleButton.scss';
|
||||||
|
|
||||||
|
interface IToggleButtonProps {
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
type?: TOGGLE_TYPE
|
||||||
|
title: string
|
||||||
|
checked: boolean
|
||||||
|
onChange: React.ChangeEventHandler<HTMLInputElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TOGGLE_TYPE {
|
||||||
|
MATERIAL,
|
||||||
|
IOS
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToggleButton: FC<IToggleButtonProps> = (props) => {
|
||||||
|
const id = `toggle-${props.id}`;
|
||||||
|
const type = props.type ?? TOGGLE_TYPE.MATERIAL;
|
||||||
|
let classLine = 'line w-10 h-4 bg-gray-400 rounded-full shadow-inner';
|
||||||
|
let classDot = 'dot absolute w-6 h-6 bg-white rounded-full shadow -left-1 -top-1 transition';
|
||||||
|
if (type === TOGGLE_TYPE.IOS) {
|
||||||
|
classLine = 'line block bg-gray-600 w-14 h-8 rounded-full';
|
||||||
|
classDot = 'dot absolute left-1 top-1 bg-white w-6 h-6 rounded-full transition';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div title={props.title}>
|
||||||
|
<div className="flex items-center justify-center w-full mb-12">
|
||||||
|
<label
|
||||||
|
htmlFor={id}
|
||||||
|
className="flex items-center cursor-pointer"
|
||||||
|
>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
id={id}
|
||||||
|
type="checkbox"
|
||||||
|
onChange={props.onChange}
|
||||||
|
checked={props.checked}
|
||||||
|
className="sr-only" />
|
||||||
|
<div className={classLine}></div>
|
||||||
|
<div className={classDot}></div>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3 text-gray-700 font-medium">
|
||||||
|
{ props.text }
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -17,6 +17,7 @@ interface IUIProps {
|
||||||
SelectContainer: (container: ContainerModel) => void
|
SelectContainer: (container: ContainerModel) => void
|
||||||
DeleteContainer: (containerId: string) => void
|
DeleteContainer: (containerId: string) => void
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
|
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, refs: Array<React.RefObject<HTMLInputElement>>) => void
|
||||||
AddContainerToSelectedContainer: (type: string) => void
|
AddContainerToSelectedContainer: (type: string) => void
|
||||||
AddContainer: (index: number, type: string, parentId: string) => void
|
AddContainer: (index: number, type: string, parentId: string) => void
|
||||||
SaveEditorAsJSON: () => void
|
SaveEditorAsJSON: () => void
|
||||||
|
@ -59,6 +60,7 @@ export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
||||||
isOpen={isElementsSidebarOpen}
|
isOpen={isElementsSidebarOpen}
|
||||||
isHistoryOpen={isHistoryOpen}
|
isHistoryOpen={isHistoryOpen}
|
||||||
OnPropertyChange={props.OnPropertyChange}
|
OnPropertyChange={props.OnPropertyChange}
|
||||||
|
OnPropertiesSubmit={props.OnPropertiesSubmit}
|
||||||
SelectContainer={props.SelectContainer}
|
SelectContainer={props.SelectContainer}
|
||||||
DeleteContainer={props.DeleteContainer}
|
DeleteContainer={props.DeleteContainer}
|
||||||
AddContainer={props.AddContainer}
|
AddContainer={props.AddContainer}
|
||||||
|
|
|
@ -23,6 +23,16 @@
|
||||||
@apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg
|
@apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.normal-btn {
|
||||||
|
@apply text-sm
|
||||||
|
py-2 px-4
|
||||||
|
rounded-full border-0
|
||||||
|
font-semibold
|
||||||
|
transition-all
|
||||||
|
bg-blue-100 text-blue-700
|
||||||
|
hover:bg-blue-200
|
||||||
|
}
|
||||||
|
|
||||||
.floating-btn {
|
.floating-btn {
|
||||||
@apply h-full w-full text-white align-middle items-center justify-center
|
@apply h-full w-full text-white align-middle items-center justify-center
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,9 @@ export const DEFAULT_MAINCONTAINER_PROPS: Properties = {
|
||||||
parentId: 'null',
|
parentId: 'null',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
isRigidBody: false,
|
|
||||||
width: DEFAULT_CONFIG.MainContainer.Width,
|
width: DEFAULT_CONFIG.MainContainer.Width,
|
||||||
height: DEFAULT_CONFIG.MainContainer.Height,
|
height: DEFAULT_CONFIG.MainContainer.Height,
|
||||||
|
isRigidBody: false,
|
||||||
fillOpacity: 0,
|
fillOpacity: 0,
|
||||||
stroke: 'black'
|
stroke: 'black'
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue