nitro-v36-with-renderer-v36
/
src /
components /
room /
widgets /
furniture /
FurnitureRecolorView.tsx
FurnitureRecolorView.tsx 6.4 Ko
Télécharger le fichier
import { ColorConverter, GetRoomEngine, GetSessionDataManager, IRoomObject, RecolorFurniComposer, RoomObjectCategory, RoomObjectVariable } from '@octane/renderer';
import { FC, useEffect, useState } from 'react';
import { FaArrowRight } from 'react-icons/fa';
import {
distinctRecolors,
isRecolorableClass,
localizeWithFallback,
NO_RECOLOR,
RECOLOR_SCOPE_THIS,
RECOLOR_SCOPES,
recolorablePalette,
recolorChannels,
recolorHex,
recolorOfData,
SendMessageComposer
} from '../../../../api';
import { Button, LayoutColorSwatchView, LayoutFurniImageView, OctaneCardContentView, OctaneCardHeaderView, OctaneCardView, Text } from '../../../../common';
import { useFurnitureRecolorWidget } from '../../../../hooks';
import { Select } from '../../../../common/Select';
import '../../../../css/room/FurniWindowsCAir.css';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
const WHITE = 0xffffff;
const SCOPE_FALLBACKS = ['Only this furni', 'Furni with the same color', 'Furni of the same type', 'Furni of the same type and color'];
const classNameOf = (object: IRoomObject): string =>
{
const typeId = object?.model?.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID) ?? 0;
return typeId > 0 ? (GetSessionDataManager().getFloorItemData(typeId)?.className ?? null) : null;
};
const recolorOf = (object: IRoomObject): number => recolorOfData(object?.model?.getValue<{ [key: string]: string }>(RoomObjectVariable.FURNITURE_DATA));
/**
* The official recolour window for recolourable Builders Club furni: the furni's palette, the colours
* already used in the room, and which furni the colour goes to.
*/
export const FurnitureRecolorView: FC = () =>
{
const { objectId = -1, close = null } = useFurnitureRecolorWidget();
const [, setTick] = useState(0);
const [selected, setSelected] = useState(NO_RECOLOR);
const [scope, setScope] = useState(RECOLOR_SCOPE_THIS);
useEffect(() =>
{
setSelected(NO_RECOLOR);
if (objectId === -1) return;
const interval = setInterval(() => setTick((value) => value + 1), 500);
return () => clearInterval(interval);
}, [objectId]);
if (objectId === -1) return null;
const roomId = GetRoomEngine().activeRoomId;
const object = GetRoomEngine().getRoomObject(roomId, objectId, RoomObjectCategory.FLOOR);
const className = classNameOf(object);
if (!object || !isRecolorableClass(className)) return null;
const typeId = object.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
const current = recolorOf(object);
const palette = recolorablePalette(className);
const roomColors = distinctRecolors(
GetRoomEngine()
.getRoomObjects(roomId, RoomObjectCategory.FLOOR)
.filter((roomObject) => isRecolorableClass(classNameOf(roomObject)))
.map((roomObject) => roomObject.model?.getValue<{ [key: string]: string }>(RoomObjectVariable.FURNITURE_DATA))
).filter((color) => palette.includes(color));
const target = selected === NO_RECOLOR ? current : selected;
const swatches = (colors: number[]) =>
colors.map((color) => (
<LayoutColorSwatchView
key={color}
active={color === target}
color={ColorConverter.int2rgb(color)}
title={recolorHex(color)}
onClick={() => setSelected(color)}
/>
));
const apply = () =>
{
if (target === NO_RECOLOR) return;
const [red, green, blue] = recolorChannels(target);
SendMessageComposer(new RecolorFurniComposer(objectId, scope, red, green, blue));
setSelected(NO_RECOLOR);
};
return (
<NativeTextTree>
<OctaneCardView className="octane-furni-recolor" theme="primary-slim">
<OctaneCardHeaderView headerText={localizeWithFallback('furniture.recolor.title', 'Recolor furniture')} onCloseClick={close} />
<OctaneCardContentView gap={2}>
<Text bold small>
{localizeWithFallback('furniture.recolor.preview', 'Preview')}
</Text>
<div className="flex items-center justify-center gap-3">
<div className="octane-color-preview-box">
<LayoutFurniImageView productClassId={typeId} productType="s" recolor={current === NO_RECOLOR ? WHITE : current} />
</div>
<FaArrowRight className="octane-color-preview-arrow" />
<div className="octane-color-preview-box">
<LayoutFurniImageView productClassId={typeId} productType="s" recolor={target === NO_RECOLOR ? WHITE : target} />
</div>
</div>
<Text bold small>
{localizeWithFallback('furniture.recolor.palette', 'Palette')}
</Text>
<div className="octane-color-swatch-grid">{swatches(palette)}</div>
<Text bold small>
{localizeWithFallback('furniture.recolor.room_colors', 'Colors in this room')}
</Text>
<div className="octane-color-swatch-grid is-room">{swatches(roomColors)}</div>
<Text bold small>
{localizeWithFallback('furniture.recolor.scope', 'Apply to:')}
</Text>
<div className="flex items-center gap-2">
<Select className="form-select form-select-sm grow" value={scope} onChange={(event) => setScope(Number(event.target.value))}>
{RECOLOR_SCOPES.map((value) => (
<option key={value} value={value}>
{localizeWithFallback(`furniture.recolor.scope.${value}`, SCOPE_FALLBACKS[value])}
</option>
))}
</Select>
<Button disabled={target === NO_RECOLOR || (target === current && scope === RECOLOR_SCOPE_THIS)} variant="success" onClick={apply}>
{localizeWithFallback('furniture.recolor.apply', 'Apply')}
</Button>
</div>
</OctaneCardContentView>
</OctaneCardView>
</NativeTextTree>
);
};
