nitro-v36-with-renderer-v36
/
src /
components /
wired /
views /
conditions /
WiredConditionSelectionQuantityView.tsx
WiredConditionSelectionQuantityView.tsx 9.8 Ko
Télécharger le fichier
import { FC, useEffect, useMemo, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import sourceFurniIcon from '../../../../assets/images/wired/source_furni.png';
import sourceUserIcon from '../../../../assets/images/wired/source_user.png';
import { Button, Slider, Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { WiredFurniSelectionSourceRow } from '../WiredFurniSelectionSourceRow';
import { sortWiredSourceOptions, useAvailableUserSources } from '../WiredSourcesSelector';
import { WiredConditionBaseView } from './WiredConditionBaseView';
import { Radio } from '../../../../common/Radio';
import { BaseButton } from '../../../../common/BaseButton';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
const SOURCE_GROUP_USERS = 0;
const SOURCE_GROUP_FURNI = 1;
const SOURCE_TRIGGER = 0;
const SOURCE_SELECTED = 100;
const COMPARISON_OPTIONS = [0, 1, 2];
const MIN_QUANTITY = 0;
const MAX_QUANTITY = 100;
const QUANTITY_PATTERN = /^\d*$/;
const USER_SOURCES = sortWiredSourceOptions(
[
{ value: 0, label: 'wiredfurni.params.sources.users.0' },
{ value: 200, label: 'wiredfurni.params.sources.users.200' },
{ value: 201, label: 'wiredfurni.params.sources.users.201' }
],
'users'
);
const FURNI_SOURCES = sortWiredSourceOptions(
[
{ value: 100, label: 'wiredfurni.params.sources.furni.100' },
{ value: 200, label: 'wiredfurni.params.sources.furni.200' },
{ value: 201, label: 'wiredfurni.params.sources.furni.201' },
{ value: 0, label: 'wiredfurni.params.sources.furni.0' }
],
'furni'
);
const SOURCE_GROUP_BUTTONS = [
{ key: 'user', icon: sourceUserIcon, isUserGroup: true },
{ key: 'furni', icon: sourceFurniIcon, isUserGroup: false }
] as const;
const clampQuantity = (value: number) => {
if (isNaN(value)) return MIN_QUANTITY;
return Math.max(MIN_QUANTITY, Math.min(MAX_QUANTITY, Math.floor(value)));
};
const normalizeSourceType = (value: number, allowed: number[]) => {
return allowed.includes(value) ? value : SOURCE_TRIGGER;
};
export const WiredConditionSelectionQuantityView: FC<{}> = () => {
// Official layout: [merged, amount, comparison], what is counted in the official furni or user source slot.
const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null, setSourceTypes = null } = useWired();
const rawAvailableUserSources = useAvailableUserSources(trigger, USER_SOURCES);
const availableUserSources = useMemo(() => sortWiredSourceOptions(rawAvailableUserSources, 'users'), [rawAvailableUserSources]);
const [comparison, setComparison] = useState(1);
const [quantity, setQuantity] = useState(0);
const [quantityInput, setQuantityInput] = useState('0');
const [sourceGroup, setSourceGroup] = useState(SOURCE_GROUP_USERS);
const [userSource, setUserSource] = useState(SOURCE_TRIGGER);
const [furniSource, setFurniSource] = useState(SOURCE_TRIGGER);
useEffect(() => {
if (!trigger) return;
const nextComparison = trigger.intData.length > 2 ? trigger.intData[2] : 1;
const nextQuantity = clampQuantity(trigger.intData.length > 1 ? trigger.intData[1] : 0);
const nextSourceGroup = (trigger.furniSourceTypes?.length ?? 0) > 0 ? SOURCE_GROUP_FURNI : SOURCE_GROUP_USERS;
const nextSourceType = (nextSourceGroup === SOURCE_GROUP_FURNI ? trigger.furniSourceTypes?.[0] : trigger.userSourceTypes?.[0]) ?? SOURCE_TRIGGER;
setComparison(COMPARISON_OPTIONS.includes(nextComparison) ? nextComparison : 1);
setQuantity(nextQuantity);
setQuantityInput(nextQuantity.toString());
setSourceGroup(nextSourceGroup);
setUserSource(
nextSourceGroup === SOURCE_GROUP_USERS
? normalizeSourceType(
nextSourceType,
availableUserSources.map((source) => source.value)
)
: SOURCE_TRIGGER
);
setFurniSource(
nextSourceGroup === SOURCE_GROUP_FURNI
? normalizeSourceType(
nextSourceType,
FURNI_SOURCES.map((source) => source.value)
)
: SOURCE_TRIGGER
);
}, [availableUserSources, trigger]);
const isUserGroup = sourceGroup === SOURCE_GROUP_USERS;
const activeSources = useMemo(() => (isUserGroup ? availableUserSources : FURNI_SOURCES), [availableUserSources, isUserGroup]);
const activeSource = isUserGroup ? userSource : furniSource;
const activeSourceIndex = Math.max(
0,
activeSources.findIndex((source) => source.value === activeSource)
);
const currentSourceType = activeSources[activeSourceIndex]?.value ?? SOURCE_TRIGGER;
const requiresFurni =
!isUserGroup && furniSource === SOURCE_SELECTED ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID : WiredFurniType.STUFF_SELECTION_OPTION_NONE;
useEffect(() => {
if (currentSourceType === activeSource) return;
if (isUserGroup) setUserSource(currentSourceType);
else setFurniSource(currentSourceType);
}, [activeSource, currentSourceType, isUserGroup]);
const changeGroup = (nextIsUserGroup: boolean) => {
if (nextIsUserGroup === isUserGroup) return;
const nextOptions = nextIsUserGroup ? availableUserSources : FURNI_SOURCES;
const nextIndex = Math.min(activeSourceIndex, Math.max(0, nextOptions.length - 1));
const nextOption = nextOptions[nextIndex] ?? nextOptions[0];
setSourceGroup(nextIsUserGroup ? SOURCE_GROUP_USERS : SOURCE_GROUP_FURNI);
if (!nextOption) return;
if (nextIsUserGroup) setUserSource(nextOption.value);
else setFurniSource(nextOption.value);
};
const updateQuantity = (value: number) => {
const nextValue = clampQuantity(value);
setQuantity(nextValue);
setQuantityInput(nextValue.toString());
};
const updateQuantityInput = (value: string) => {
if (!QUANTITY_PATTERN.test(value)) return;
setQuantityInput(value);
if (!value.length) {
setQuantity(0);
return;
}
updateQuantity(parseInt(value));
};
const save = () => {
setIntParams([0, clampQuantity(quantity), comparison]);
setStringParam('');
setSourceTypes(isUserGroup ? { furni: [], users: [userSource] } : { furni: [furniSource], users: [] });
if (requiresFurni <= WiredFurniType.STUFF_SELECTION_OPTION_NONE) setFurniIds([]);
};
return (
<NativeTextTree>
<WiredConditionBaseView hasSpecialInput={true} requiresFurni={requiresFurni} save={save}>
<div className="flex flex-col gap-1">
<Text bold>{LocalizeText('wiredfurni.params.comparison_selection')}</Text>
{COMPARISON_OPTIONS.map((value) => {
return (
<label key={value} className="flex items-center gap-1">
<Radio checked={comparison === value} name="selectionQuantityComparison" onCheckedChange={() => setComparison(value)} />
<Text>{LocalizeText(`wiredfurni.params.comparison.${value}`)}</Text>
</label>
);
})}
</div>
<div className="flex flex-col gap-2">
<input
className="form-control form-control-sm"
inputMode="numeric"
max={MAX_QUANTITY}
min={MIN_QUANTITY}
type="text"
value={quantityInput}
onBlur={() => setQuantityInput(clampQuantity(quantity).toString())}
onChange={(event) => updateQuantityInput(event.target.value)}
/>
<Slider max={MAX_QUANTITY} min={MIN_QUANTITY} step={1} value={quantity} onChange={(event) => updateQuantity(event as number)} />
<Text small>{quantity}</Text>
</div>
<WiredFurniSelectionSourceRow
title="wiredfurni.params.sources.merged.title"
options={activeSources}
value={activeSource}
selectionKind={isUserGroup ? 'primary' : 'secondary'}
selectionActive={!isUserGroup && furniSource === SOURCE_SELECTED}
selectionCount={furniIds.length}
selectionLimit={trigger?.maximumItemSelectionCount ?? 20}
selectionEnabledValues={[SOURCE_SELECTED]}
showSelectionToggle={false}
headerContent={
<div className="octane-wired__give-var-targets">
{SOURCE_GROUP_BUTTONS.map((button) => (
<BaseButton
key={button.key}
type="button"
className={`octane-wired__give-var-target octane-wired__give-var-target--${button.key} ${isUserGroup === button.isUserGroup ? 'is-active' : ''}`}
onClick={() => changeGroup(button.isUserGroup)}
>
<img src={button.icon} alt={button.key} />
</BaseButton>
))}
</div>
}
onChange={(value) => {
if (isUserGroup) setUserSource(value);
else setFurniSource(value);
}}
/>
</WiredConditionBaseView>
</NativeTextTree>
);
};
