nitro-v36-with-renderer-v36
/
src /
components /
wired /
views /
conditions /
WiredConditionHasVariableView.tsx
WiredConditionHasVariableView.tsx 10.8 Ko
Télécharger le fichier
import { FC, useEffect, useMemo, useState } from 'react';
import { fromOfficialVariableTarget, LocalizeText, localizeWithFallback, toOfficialVariableTarget, WiredFurniType } from '../../../../api';
import contextVariableIcon from '../../../../assets/images/wired/var/icon_source_context_clean.png';
import furniVariableIcon from '../../../../assets/images/wired/var/icon_source_furni.png';
import userVariableIcon from '../../../../assets/images/wired/var/icon_source_user.png';
import { Text } from '../../../../common';
import { useWired, useWiredTools } from '../../../../hooks';
import { WiredFurniSelectionSourceRow } from '../WiredFurniSelectionSourceRow';
import { FURNI_SOURCES, sortWiredSourceOptions, USER_SOURCES, useAvailableUserSources } from '../WiredSourcesSelector';
import { WiredVariablePicker } from '../WiredVariablePicker';
import {
buildWiredVariablePickerEntries,
createFallbackVariableEntry,
flattenWiredVariablePickerEntries,
normalizeVariableTokenFromWire
} from '../WiredVariablePickerData';
import { readWiredVariableToken, toWiredVariableId, wiredVariableIdList } from '../WiredVariableIds';
import { WiredConditionBaseView } from './WiredConditionBaseView';
import { BaseButton } from '../../../../common/BaseButton';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
type ConditionVariableTargetType = 'user' | 'furni' | 'context';
interface IVariableDefinition {
availability: number;
hasValue: boolean;
itemId: number;
name: string;
}
const TARGET_USER = 0;
const TARGET_FURNI = 1;
const TARGET_CONTEXT = 2;
const SOURCE_TRIGGER = 0;
const SOURCE_SELECTED = 100;
const TARGET_BUTTONS: Array<{ key: ConditionVariableTargetType; icon: string; disabled?: boolean }> = [
{ key: 'furni', icon: furniVariableIcon },
{ key: 'user', icon: userVariableIcon },
{ key: 'context', icon: contextVariableIcon }
];
const CONTEXT_SOURCE_OPTIONS = [{ value: SOURCE_TRIGGER, label: localizeWithFallback('wiredfurni.params.sources.context', 'Current execution') }];
const getTargetValue = (value: ConditionVariableTargetType) => {
switch (value) {
case 'furni':
return TARGET_FURNI;
case 'context':
return TARGET_CONTEXT;
default:
return TARGET_USER;
}
};
const normalizeTargetType = (value: number): ConditionVariableTargetType => {
switch (value) {
case TARGET_FURNI:
return 'furni';
case TARGET_CONTEXT:
return 'context';
default:
return 'user';
}
};
const getTargetDefinitions = (
targetType: ConditionVariableTargetType,
userDefinitions: IVariableDefinition[],
furniDefinitions: IVariableDefinition[],
contextDefinitions: IVariableDefinition[]
) => {
switch (targetType) {
case 'furni':
return furniDefinitions;
case 'context':
return contextDefinitions;
default:
return userDefinitions;
}
};
const getSourceTitle = (targetType: ConditionVariableTargetType) => {
switch (targetType) {
case 'furni':
return LocalizeText('wiredfurni.params.sources.furni.title');
case 'context':
return LocalizeText('wiredfurni.params.sources.merged.title.variables');
default:
return LocalizeText('wiredfurni.params.sources.users.title');
}
};
export const WiredConditionHasVariableView: FC = () => {
// Official layout: [variable target], the furni and users in the official source slots; the quantifier
// (labelled `.neg` for the not twin, as the server marks it) is the base view's.
const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null, setSourceTypes = null, setVariableIds = null } = useWired();
const { userVariableDefinitions = [], furniVariableDefinitions = [], contextVariableDefinitions = [] } = useWiredTools();
const [targetType, setTargetType] = useState<ConditionVariableTargetType>('user');
const [variableToken, setVariableToken] = useState('');
const [userSource, setUserSource] = useState(SOURCE_TRIGGER);
const [furniSource, setFurniSource] = useState(SOURCE_TRIGGER);
const availableUserSources = useAvailableUserSources(trigger, USER_SOURCES);
const orderedUserSources = useMemo(() => sortWiredSourceOptions(availableUserSources, 'users'), [availableUserSources]);
const orderedFurniSources = useMemo(() => sortWiredSourceOptions(FURNI_SOURCES, 'furni'), []);
const variableDefinitions = useMemo(
() => getTargetDefinitions(targetType, userVariableDefinitions, furniVariableDefinitions, contextVariableDefinitions),
[contextVariableDefinitions, furniVariableDefinitions, targetType, userVariableDefinitions]
);
const variableEntries = useMemo(() => buildWiredVariablePickerEntries(targetType, 'condition', variableDefinitions), [targetType, variableDefinitions]);
const resolvedVariableEntries = useMemo(() => {
if (!variableToken) return variableEntries;
if (flattenWiredVariablePickerEntries(variableEntries).some((entry) => entry.token === variableToken)) return variableEntries;
const fallbackEntry = createFallbackVariableEntry(targetType, variableToken);
return fallbackEntry ? [fallbackEntry, ...variableEntries] : variableEntries;
}, [targetType, variableEntries, variableToken]);
const sourceOptions = useMemo(() => {
switch (targetType) {
case 'furni':
return orderedFurniSources;
case 'context':
return CONTEXT_SOURCE_OPTIONS;
default:
return orderedUserSources;
}
}, [orderedFurniSources, orderedUserSources, targetType]);
const sourceValue = targetType === 'furni' ? furniSource : targetType === 'user' ? userSource : SOURCE_TRIGGER;
const requiresFurni =
targetType === 'furni' && furniSource === SOURCE_SELECTED ? WiredFurniType.STUFF_SELECTION_OPTION_BY_ID : WiredFurniType.STUFF_SELECTION_OPTION_NONE;
const selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
useEffect(() => {
if (!trigger) return;
const intData = trigger.intData || [];
const nextTargetType = normalizeTargetType(intData.length > 0 ? fromOfficialVariableTarget(intData[0]) : TARGET_USER);
const nextUserSource = trigger.userSourceTypes?.[0] ?? SOURCE_TRIGGER;
const nextFurniSource = trigger.furniSourceTypes?.[0] ?? ((trigger.selectedItems?.length ?? 0) > 0 ? SOURCE_SELECTED : SOURCE_TRIGGER);
setTargetType(nextTargetType);
// AIR: the variable is the first variable id; an older server wrote the token as the string.
setVariableToken(readWiredVariableToken(trigger.variableIds, 0, normalizeVariableTokenFromWire((trigger.stringData || '').split('\t')[0])));
setUserSource(nextUserSource);
setFurniSource(nextFurniSource);
}, [trigger]);
useEffect(() => {
if (targetType !== 'user') return;
if (orderedUserSources.some((option) => option.value === userSource)) return;
setUserSource(SOURCE_TRIGGER);
}, [orderedUserSources, targetType, userSource]);
const save = () => {
setStringParam('');
setVariableIds(wiredVariableIdList(toWiredVariableId(targetType, variableToken)));
setIntParams([toOfficialVariableTarget(getTargetValue(targetType))]);
setSourceTypes({ furni: [furniSource], users: [userSource] });
if (requiresFurni <= WiredFurniType.STUFF_SELECTION_OPTION_NONE) setFurniIds([]);
};
// AIR saves an empty variable; the condition then passes only when negated.
const validate = () => true;
const handleTargetChange = (nextTargetType: ConditionVariableTargetType) => {
if (nextTargetType === targetType) return;
setTargetType(nextTargetType);
setVariableToken('');
};
return (
<NativeTextTree>
<WiredConditionBaseView
hasSpecialInput={true}
requiresFurni={requiresFurni}
save={save}
validate={validate}
footerCollapsible={false}
footer={
<div className="flex flex-col gap-2">
<WiredFurniSelectionSourceRow
title={getSourceTitle(targetType)}
titleIsLiteral={true}
options={sourceOptions}
value={sourceValue}
selectionKind="primary"
selectionActive={requiresFurni > WiredFurniType.STUFF_SELECTION_OPTION_NONE}
selectionCount={furniIds.length}
selectionLimit={selectionLimit}
selectionEnabledValues={[SOURCE_SELECTED]}
showSelectionToggle={false}
onChange={(value) => {
if (targetType === 'furni') {
setFurniSource(value);
return;
}
if (targetType === 'user') {
setUserSource(value);
return;
}
}}
/>
</div>
}
>
<div className="octane-wired__give-var">
<div className="octane-wired__give-var-heading">
<Text>{LocalizeText('wiredfurni.params.variables.variable_selection')}</Text>
<div className="octane-wired__give-var-targets">
{TARGET_BUTTONS.map((button) => (
<BaseButton
key={button.key}
type="button"
disabled={button.disabled}
className={`octane-wired__give-var-target octane-wired__give-var-target--${button.key} ${targetType === button.key ? 'is-active' : ''}`}
onClick={() => handleTargetChange(button.key)}
>
<img src={button.icon} alt={button.key} />
</BaseButton>
))}
</div>
</div>
<WiredVariablePicker
entries={resolvedVariableEntries}
recentScope="variable-conditions"
selectedToken={variableToken}
onSelect={(entry) => setVariableToken(entry.token)}
/>
</div>
</WiredConditionBaseView>
</NativeTextTree>
);
};
