More camera changes

This commit is contained in:
Bill 2021-08-07 19:05:31 -04:00
parent 47c0ab6c44
commit a3350b8b54
11 changed files with 240 additions and 174 deletions

View File

@ -87,6 +87,53 @@
.nitro-camera-editor { .nitro-camera-editor {
width: 600px; width: 600px;
.content-area {
min-height: 350px;
height: 350px;
resize: vertical;
.picture-preview {
width: 320px;
height: 320px;
.slider {
background: linear-gradient(180deg, transparent, black);
text-shadow: 1px 1px rgba(0, 0, 0, .5);
}
}
}
.effect-grid {
.grid-item-container {
height: 60px !important;
max-height: 60px !important;
.grid-item {
overflow: unset;
.remove-effect {
position: absolute;
top: -1px;
right: -1px;
padding: 2px;
font-size: 11px;
line-height: 11px;
min-height: unset;
}
.effect-thumbnail-image {
img {
width: 40px;
height: 40px;
image-rendering: auto;
}
}
}
}
}
.effects { .effects {
height: 363px; height: 363px;
min-height: 363px; min-height: 363px;
@ -117,16 +164,6 @@
} }
} }
} }
.picture-preview {
width: 320px;
height: 320px;
.slider {
background: linear-gradient(180deg, transparent, black);
text-shadow: 1px 1px rgba(0, 0, 0, .5);
}
}
} }
.nitro-camera-checkout { .nitro-camera-checkout {

View File

@ -24,7 +24,7 @@ export const CameraWidgetView: FC<{}> = props =>
const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1); const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1);
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]); const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
const [ isZoomed, setIsZoomed ] = useState(false); const [ isZoomed, setIsZoomed ] = useState(false);
const [ myLevel, setMyLevel ] = useState(10); const [ myLevel, setMyLevel ] = useState(1);
const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null); const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null);
const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) => const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) =>
@ -111,7 +111,7 @@ export const CameraWidgetView: FC<{}> = props =>
return ( return (
<CameraWidgetContextProvider value={ { cameraRoll, setCameraRoll, selectedPictureIndex, setSelectedPictureIndex, selectedEffects, setSelectedEffects, isZoomed, setIsZoomed } }> <CameraWidgetContextProvider value={ { cameraRoll, setCameraRoll, selectedPictureIndex, setSelectedPictureIndex, selectedEffects, setSelectedEffects, isZoomed, setIsZoomed } }>
{ (mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={ () => processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> } { (mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={ () => processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> }
{ (mode === MODE_EDITOR) && <CameraWidgetEditorView myLevel={ myLevel } onClose={ () => processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> } { (mode === MODE_EDITOR) && <CameraWidgetEditorView picture={ cameraRoll[selectedPictureIndex] } myLevel={ myLevel } onClose={ () => processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> }
{ (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView onCloseClick={ () => processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }></CameraWidgetCheckoutView> } { (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView onCloseClick={ () => processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }></CameraWidgetCheckoutView> }
</CameraWidgetContextProvider> </CameraWidgetContextProvider>
); );

View File

@ -0,0 +1,6 @@
export class CameraPictureThumbnail
{
constructor(
public effectName: string,
public thumbnailUrl: string) {}
}

View File

@ -1,17 +1,17 @@
import { IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; import { IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
import { ProviderProps } from 'react'; import { Dispatch, ProviderProps, SetStateAction } from 'react';
import { CameraPicture } from '../common/CameraPicture'; import { CameraPicture } from '../common/CameraPicture';
export interface ICameraWidgetContext export interface ICameraWidgetContext
{ {
cameraRoll: CameraPicture[], cameraRoll: CameraPicture[],
setCameraRoll: (cameraRoll: CameraPicture[]) => void, setCameraRoll: Dispatch<SetStateAction<CameraPicture[]>>;
selectedPictureIndex: number, selectedPictureIndex: number,
setSelectedPictureIndex: (index: number) => void, setSelectedPictureIndex: Dispatch<SetStateAction<number>>;
selectedEffects: IRoomCameraWidgetSelectedEffect[], selectedEffects: IRoomCameraWidgetSelectedEffect[],
setSelectedEffects: (selectedEffects: IRoomCameraWidgetSelectedEffect[]) => void, setSelectedEffects: Dispatch<SetStateAction<IRoomCameraWidgetSelectedEffect[]>>;
isZoomed: boolean, isZoomed: boolean,
setIsZoomed: (isZoomed: boolean) => void setIsZoomed: Dispatch<SetStateAction<boolean>>;
} }
export interface CameraWidgetContextProps extends ProviderProps<ICameraWidgetContext> export interface CameraWidgetContextProps extends ProviderProps<ICameraWidgetContext>

View File

@ -1,94 +1,85 @@
import { RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; import { IRoomCameraWidgetSelectedEffect, RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
import classNames from 'classnames'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { FC, useCallback, useEffect, useState } from 'react';
import { GetRoomCameraWidgetManager } from '../../../../../../api'; import { GetRoomCameraWidgetManager } from '../../../../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../../../layout';
import { LocalizeText } from '../../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../../utils/LocalizeText';
import { useCameraWidgetContext } from '../../context/CameraWidgetContext'; import { CameraPictureThumbnail } from '../../common/CameraPictureThumbnail';
import { CameraWidgetEditorTabs, CameraWidgetEditorViewProps } from './CameraWidgetEditorView.types'; import { CameraWidgetEditorTabs, CameraWidgetEditorViewProps } from './CameraWidgetEditorView.types';
import { CameraWidgetEffectListView } from './effect-list/CameraWidgetEffectListView';
const TABS: string[] = [ CameraWidgetEditorTabs.COLORMATRIX, CameraWidgetEditorTabs.COMPOSITE ]; const TABS: string[] = [ CameraWidgetEditorTabs.COLORMATRIX, CameraWidgetEditorTabs.COMPOSITE ];
export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props => export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
{ {
const { availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props; const { picture = null, availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props;
const [ currentTab, setCurrentTab ] = useState(TABS[0]); const [ currentTab, setCurrentTab ] = useState(TABS[0]);
const [ selectedEffectName, setSelectedEffectName ] = useState(null); const [ selectedEffectName, setSelectedEffectName ] = useState<string>(null);
const [ effectsThumbnails, setEffectsThumbnails ] = useState<{ name: string, image: HTMLImageElement }[]>([]); const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
const { cameraRoll = null, selectedPictureIndex = -1, selectedEffects = null, isZoomed = false, setSelectedEffects = null, setIsZoomed = null } = useCameraWidgetContext(); const [ effectsThumbnails, setEffectsThumbnails ] = useState<CameraPictureThumbnail[]>([]);
const [ isZoomed, setIsZoomed ] = useState(false);
useEffect(() => const getColorMatrixEffects = useMemo(() =>
{ {
const thumbnails = []; return availableEffects.filter(effect => effect.colorMatrix);
}, [ availableEffects ]);
for(const effect of availableEffects) const getCompositeEffects = useMemo(() =>
{ {
thumbnails.push({name: effect.name, image: GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false)}); return availableEffects.filter(effect => effect.texture);
} }, [ availableEffects ]);
setEffectsThumbnails(thumbnails);
}, [ cameraRoll, selectedPictureIndex, availableEffects ]);
const getEffectThumbnail = useCallback((effectName: string) =>
{
const search = effectsThumbnails.find(thumbnail => thumbnail.name === effectName);
if(search) return search.image.src;
return null;
}, [ effectsThumbnails ]);
const getEffectList = useCallback(() => const getEffectList = useCallback(() =>
{ {
if(currentTab === CameraWidgetEditorTabs.COLORMATRIX) if(currentTab === CameraWidgetEditorTabs.COLORMATRIX)
{ {
return availableEffects.filter(effect => effect.colorMatrix); return getColorMatrixEffects;
} }
else
return getCompositeEffects;
}, [ currentTab, getColorMatrixEffects, getCompositeEffects ]);
const getSelectedEffectIndex = useCallback((name: string) =>
{ {
return availableEffects.filter(effect => effect.texture); if(!name || !name.length || !selectedEffects || !selectedEffects.length) return -1;
}
}, [ currentTab, availableEffects ]);
const getCurrentPicture = useCallback(() => return selectedEffects.findIndex(effect => (effect.effect.name === name));
{
return GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, selectedEffects, isZoomed);
}, [ cameraRoll, selectedPictureIndex, selectedEffects, isZoomed ]);
const getCurrentEffectAlpha = useCallback(() =>
{
if(!selectedEffectName) return 0;
const selectedEffect = selectedEffects.find(effect => effect.effect.name === selectedEffectName);
if(!selectedEffect) return 0;
return selectedEffect.alpha;
}, [ selectedEffectName, selectedEffects ]);
const getEffectIndex = useCallback((effectName) =>
{
return selectedEffects.findIndex(effect => effect.effect.name === effectName);
}, [ selectedEffects ]) }, [ selectedEffects ])
const setSelectedEffectAlpha = useCallback((newAlpha: number) => const getCurrentEffectIndex = useMemo(() =>
{ {
if(!selectedEffectName) return; return getSelectedEffectIndex(selectedEffectName)
}, [ selectedEffectName, getSelectedEffectIndex ])
const selectedEffectIndex = getEffectIndex(selectedEffectName); const getCurrentEffect = useMemo(() =>
{
if(!selectedEffectName) return null;
if(selectedEffectIndex === -1) return; return (selectedEffects[getCurrentEffectIndex] || null);
}, [ selectedEffectName, getCurrentEffectIndex, selectedEffects ]);
const clone = Array.from(selectedEffects); const setCurrentEffectAlpha = useCallback((alpha: number) =>
{
const index = getCurrentEffectIndex;
const selectedEffect = clone[selectedEffectIndex]; if(index === -1) return;
clone[selectedEffectIndex] = new RoomCameraWidgetSelectedEffect(selectedEffect.effect, newAlpha); setSelectedEffects(prevValue =>
{
const clone = [ ...prevValue ];
const currentEffect = clone[index];
setSelectedEffects(clone); clone[getCurrentEffectIndex] = new RoomCameraWidgetSelectedEffect(currentEffect.effect, alpha);
}, [ selectedEffectName, getEffectIndex, selectedEffects, setSelectedEffects ]);
const processAction = useCallback((type: string, value: string | number = null) => return clone;
});
}, [ getCurrentEffectIndex, setSelectedEffects ]);
const getCurrentPictureUrl = useMemo(() =>
{
return GetRoomCameraWidgetManager().applyEffects(picture.texture, selectedEffects, isZoomed).src;
}, [ picture, selectedEffects, isZoomed ]);
const processAction = useCallback((type: string, effectName: string = null) =>
{ {
switch(type) switch(type)
{ {
@ -102,78 +93,70 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
onCheckout(); onCheckout();
return; return;
case 'change_tab': case 'change_tab':
setCurrentTab(String(value)); setCurrentTab(String(effectName));
return; return;
case 'select_effect': case 'select_effect': {
let existingIndex = getSelectedEffectIndex(effectName);
if(existingIndex >= 0) return;
const effect = availableEffects.find(effect => (effect.name === effectName));
if(!effect) return;
setSelectedEffects(prevValue =>
{ {
let existingIndex = -1; return [ ...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1) ];
});
if(selectedEffects.length > 0) setSelectedEffectName(effect.name);
{
existingIndex = getEffectIndex(value);
}
let effect = null;
if(existingIndex === -1)
{
effect = availableEffects.find(effect => effect.name === value);
if(effect.minLevel > myLevel) return;
setSelectedEffects([...selectedEffects, new RoomCameraWidgetSelectedEffect(effect, 0.5)]);
}
if(effect && effect.minLevel > myLevel) return;
if(selectedEffectName !== value)
{
setSelectedEffectName(value);
}
else
{
setSelectedEffectName(null);
}
}
return; return;
case 'remove_effect':
{
const existingIndex = getEffectIndex(value);
if(existingIndex > -1)
{
const effect = selectedEffects[existingIndex];
if(effect.effect.name === selectedEffectName)
{
setSelectedEffectName(null);
} }
case 'remove_effect': {
let existingIndex = getSelectedEffectIndex(effectName);
if(existingIndex === -1) return;
setSelectedEffects(prevValue =>
{
const clone = [ ...prevValue ];
const clone = Array.from(selectedEffects);
clone.splice(existingIndex, 1); clone.splice(existingIndex, 1);
setSelectedEffects(clone); return clone;
} });
}
if(selectedEffectName === effectName) setSelectedEffectName(null);
return; return;
}
case 'clear_effects': case 'clear_effects':
setSelectedEffectName(null); setSelectedEffectName(null);
setSelectedEffects([]); setSelectedEffects([]);
return; return;
case 'download': case 'download':
window.open(getCurrentPicture().src, '_blank'); //window.open(getCurrentPicture().src, '_blank');
return; return;
case 'zoom': case 'zoom':
setIsZoomed(!isZoomed); setIsZoomed(!isZoomed);
return; return;
} }
}, [ onClose, onCancel, onCheckout, getCurrentPicture, myLevel, selectedEffectName, getEffectIndex, availableEffects, isZoomed, setIsZoomed, selectedEffects, setSelectedEffects ]); }, [ isZoomed, availableEffects, getSelectedEffectIndex, selectedEffectName, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects ]);
useEffect(() =>
{
const thumbnails: CameraPictureThumbnail[] = [];
for(const effect of availableEffects)
{
thumbnails.push(new CameraPictureThumbnail(effect.name, GetRoomCameraWidgetManager().applyEffects(picture.texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false).src));
}
setEffectsThumbnails(thumbnails);
}, [ picture, availableEffects ]);
return ( return (
<NitroCardView className="nitro-camera-editor"> <NitroCardView className="nitro-camera-editor">
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } /> <NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
<div className="d-flex">
<div className="w-100">
<NitroCardTabsView> <NitroCardTabsView>
{ TABS.map(tab => { TABS.map(tab =>
{ {
@ -181,52 +164,17 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
}) } }) }
</NitroCardTabsView> </NitroCardTabsView>
<NitroCardContentView> <NitroCardContentView>
<div className="d-flex h-100 overflow-auto effects px-2"> <div className="row h-100">
<div className="row row-cols-3"> <div className="col-5 d-flex flex-column h-100">
{ getEffectList().map(effect => <CameraWidgetEffectListView myLevel={ myLevel } selectedEffects={ selectedEffects } effects={ getEffectList() } thumbnails={ effectsThumbnails } processAction={ processAction } />
{
return (
<div key={ effect.name } className="col mb-3 position-relative">
{ getEffectIndex(effect.name) > -1 && <button className="btn btn-danger btn-sm p-0 position-absolute btn-remove-effect" onClick={ event => processAction('remove_effect', effect.name) }><i className="fas fa-times"></i></button> }
<div title={ effect.minLevel <= myLevel ? LocalizeText('camera.effect.name.' + effect.name) : LocalizeText('camera.effect.required.level') + ' ' + effect.minLevel } onClick={ event => processAction('select_effect', effect.name) } className={"effect-thumbnail cursor-pointer position-relative border border-2 rounded d-flex flex-column justify-content-center align-items-center py-1" + classNames({' active': selectedEffectName === effect.name})}>
{ effect.minLevel <= myLevel && <div className="effect-thumbnail-image border">
<img alt="" src={ getEffectThumbnail(effect.name) } />
</div> }
{ effect.minLevel > myLevel && <div className="text-center text-black">
<div><i className="fas fa-lock"></i></div>
<div className="fw-bold">{ effect.minLevel }</div>
</div> }
</div> </div>
<div className="col-7 d-flex flex-column h-100">
<div className="picture-preview">
<img alt="" src={ getCurrentPictureUrl } />
</div> </div>
);
}) }
</div> </div>
</div> </div>
</NitroCardContentView> </NitroCardContentView>
</div>
<div className="w-100">
<NitroCardTabsView></NitroCardTabsView>
<NitroCardContentView>
<div className="d-flex align-items-end picture-preview border" style={ { backgroundImage: 'url(' + getCurrentPicture().src + ')' } }>
{ selectedEffectName && <div className="w-100 p-2 d-flex flex-column justify-content-center slider">
<div className="w-100 text-center">{ LocalizeText('camera.effect.name.' + selectedEffectName) + ' - ' + getCurrentEffectAlpha() }</div>
<input type="range" min="0" max="1" step="0.01" value={ getCurrentEffectAlpha() } onChange={ event => setSelectedEffectAlpha(Number(event.target.value)) } className="form-range w-100" />
</div> }
</div>
<div className="d-flex justify-content-between mt-2">
<div className="btn-group">
<button className="btn btn-primary" onClick={ event => processAction('clear_effects') }><i className="fas fa-trash"></i></button>
<button className="btn btn-primary" onClick={ event => processAction('download') }><i className="fas fa-save"></i></button>
<button className="btn btn-primary" onClick={ event => processAction('zoom') }><i className={"fas " + classNames({'fa-search-plus': !isZoomed, 'fa-search-minus': isZoomed})}></i></button>
</div>
<div className="d-flex justify-content-end">
<button className="btn btn-primary me-2" onClick={ event => processAction('cancel') }>{ LocalizeText('generic.cancel') }</button>
<button className="btn btn-success" onClick={ event => processAction('checkout') }>{ LocalizeText('camera.preview.button.text') }</button>
</div>
</div>
</NitroCardContentView>
</div>
</div>
</NitroCardView> </NitroCardView>
); );
} }

View File

@ -1,7 +1,9 @@
import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer'; import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer';
import { CameraPicture } from '../../common/CameraPicture';
export interface CameraWidgetEditorViewProps export interface CameraWidgetEditorViewProps
{ {
picture: CameraPicture;
availableEffects: IRoomCameraWidgetEffect[]; availableEffects: IRoomCameraWidgetEffect[];
myLevel: number; myLevel: number;
onClose: () => void; onClose: () => void;

View File

@ -0,0 +1,29 @@
import { FC } from 'react';
import { NitroCardGridItemView } from '../../../../../../../layout/card/grid/item/NitroCardGridItemView';
import { LocalizeText } from '../../../../../../../utils';
import { CameraWidgetEffectListItemViewProps } from './CameraWidgetEffectListItemView.types';
export const CameraWidgetEffectListItemView: FC<CameraWidgetEffectListItemViewProps> = props =>
{
const { effect = null, thumbnailUrl = null, isActive = false, isLocked = false, selectEffect = null, removeEffect = null } = props;
return (
<NitroCardGridItemView title={ LocalizeText(!isLocked ? (`camera.effect.name.${ effect.name }`) : `camera.effect.required.level ${ effect.minLevel }`) } itemActive={ isActive } onClick={ event => (!isActive && selectEffect()) }>
{ isActive &&
<button className="btn btn-danger btn-sm rounded-circle remove-effect" onClick={ removeEffect }>
<i className="fas fa-times" />
</button> }
{ !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) &&
<div className="effect-thumbnail-image border">
<img alt="" src={ thumbnailUrl } />
</div> }
{ isLocked &&
<div className="text-center text-black">
<div>
<i className="fas fa-lock" />
</div>
<div className="fw-bold">{ effect.minLevel }</div>
</div> }
</NitroCardGridItemView>
);
}

View File

@ -0,0 +1,11 @@
import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer';
export interface CameraWidgetEffectListItemViewProps
{
effect: IRoomCameraWidgetEffect;
thumbnailUrl: string;
isActive: boolean;
isLocked: boolean;
selectEffect: () => void;
removeEffect: () => void;
}

View File

@ -0,0 +1,21 @@
import { FC } from 'react';
import { NitroCardGridView } from '../../../../../../../layout/card/grid/NitroCardGridView';
import { CameraWidgetEffectListItemView } from '../effect-list-item/CameraWidgetEffectListItemView';
import { CameraWidgetEffectListViewProps } from './CameraWidgetEffectListView.types';
export const CameraWidgetEffectListView: FC<CameraWidgetEffectListViewProps> = props =>
{
const { myLevel = 0, selectedEffects = [], effects = [], thumbnails = [], processAction = null } = props;
return (
<NitroCardGridView className="effect-grid" columns={ 3 }>
{ effects && (effects.length > 0) && effects.map((effect, index) =>
{
const thumbnailUrl = (thumbnails.find(thumbnail => (thumbnail.effectName === effect.name)));
const isActive = (selectedEffects.findIndex(selectedEffect => (selectedEffect.effect.name === effect.name)) > -1);
return <CameraWidgetEffectListItemView key={ index } effect={ effect } thumbnailUrl={ ((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null) } isActive={ isActive } isLocked={ (effect.minLevel > myLevel) } selectEffect={ () => processAction('select_effect', effect.name) } removeEffect={ () => processAction('remove_effect', effect.name) } />
}) }
</NitroCardGridView>
);
}

View File

@ -0,0 +1,11 @@
import { IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
import { CameraPictureThumbnail } from '../../../common/CameraPictureThumbnail';
export interface CameraWidgetEffectListViewProps
{
myLevel: number;
selectedEffects: IRoomCameraWidgetSelectedEffect[];
effects: IRoomCameraWidgetEffect[];
thumbnails: CameraPictureThumbnail[];
processAction: (type: string, name: string) => void;
}

View File

@ -23,6 +23,7 @@
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": [ "include": [
"src" "src",
"node_modules/@nitrots/nitro-renderer/src/**/*.ts",
] ]
} }