mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
336 lines
16 KiB
TypeScript
336 lines
16 KiB
TypeScript
import { FC, useEffect, useMemo, useState } from 'react';
|
|
import { LocalizeText, 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 globalVariableIcon from '../../../../assets/images/wired/var/icon_source_global.png';
|
|
import userVariableIcon from '../../../../assets/images/wired/var/icon_source_user.png';
|
|
import { Text } from '../../../../common';
|
|
import { useWired, useWiredTools } from '../../../../hooks';
|
|
import { NitroInput } from '../../../../layout';
|
|
import { WiredFurniSelectionSourceRow } from '../WiredFurniSelectionSourceRow';
|
|
import { WiredVariablePicker } from '../WiredVariablePicker';
|
|
import { createFallbackVariableEntry, flattenWiredVariablePickerEntries, IWiredVariablePickerEntry, normalizeVariableTokenFromWire } from '../WiredVariablePickerData';
|
|
import { FURNI_SOURCES, sortWiredSourceOptions, USER_SOURCES, useAvailableUserSources, WiredSourceOption } from '../WiredSourcesSelector';
|
|
import { WiredConditionBaseView } from './WiredConditionBaseView';
|
|
|
|
type VariableTargetType = 'user' | 'furni' | 'global' | 'context';
|
|
|
|
interface IVariableDefinition
|
|
{
|
|
availability: number;
|
|
hasValue: boolean;
|
|
itemId: number;
|
|
name: string;
|
|
}
|
|
|
|
const TARGET_USER = 0;
|
|
const TARGET_FURNI = 1;
|
|
const TARGET_CONTEXT = 2;
|
|
const TARGET_GLOBAL = 3;
|
|
const COMPARE_VALUE_CREATED = 0;
|
|
const COMPARE_VALUE_UPDATED = 1;
|
|
const COMPARISON_LOWER_THAN = 0;
|
|
const COMPARISON_HIGHER_THAN = 2;
|
|
const SOURCE_TRIGGER = 0;
|
|
const SOURCE_SELECTED = 100;
|
|
const QUANTIFIER_ALL = 0;
|
|
const QUANTIFIER_ANY = 1;
|
|
|
|
const TARGET_BUTTONS: Array<{ key: VariableTargetType; icon: string; disabled?: boolean; }> = [
|
|
{ key: 'furni', icon: furniVariableIcon },
|
|
{ key: 'user', icon: userVariableIcon },
|
|
{ key: 'global', icon: globalVariableIcon },
|
|
{ key: 'context', icon: contextVariableIcon }
|
|
];
|
|
|
|
const GLOBAL_SOURCE_OPTIONS: WiredSourceOption[] = [ { value: SOURCE_TRIGGER, label: 'wiredfurni.params.sources.global' } ];
|
|
const CONTEXT_SOURCE_OPTIONS: WiredSourceOption[] = [ { value: SOURCE_TRIGGER, label: 'Current execution' } ];
|
|
const COMPARE_VALUE_OPTIONS = [ COMPARE_VALUE_CREATED, COMPARE_VALUE_UPDATED ];
|
|
const COMPARISON_OPTIONS = [ COMPARISON_LOWER_THAN, COMPARISON_HIGHER_THAN ];
|
|
const DURATION_UNITS = [ 0, 1, 2, 3, 4, 5, 6, 7 ];
|
|
|
|
const getTargetValue = (value: VariableTargetType) =>
|
|
{
|
|
switch(value)
|
|
{
|
|
case 'furni': return TARGET_FURNI;
|
|
case 'global': return TARGET_GLOBAL;
|
|
case 'context': return TARGET_CONTEXT;
|
|
default: return TARGET_USER;
|
|
}
|
|
};
|
|
|
|
const normalizeTargetType = (value: number): VariableTargetType =>
|
|
{
|
|
switch(value)
|
|
{
|
|
case TARGET_FURNI: return 'furni';
|
|
case TARGET_GLOBAL: return 'global';
|
|
case TARGET_CONTEXT: return 'context';
|
|
default: return 'user';
|
|
}
|
|
};
|
|
|
|
const getTargetDefinitions = (targetType: VariableTargetType, userDefinitions: IVariableDefinition[], furniDefinitions: IVariableDefinition[], roomDefinitions: IVariableDefinition[], contextDefinitions: IVariableDefinition[]) =>
|
|
{
|
|
switch(targetType)
|
|
{
|
|
case 'furni': return furniDefinitions;
|
|
case 'global': return roomDefinitions;
|
|
case 'context': return contextDefinitions;
|
|
default: return userDefinitions;
|
|
}
|
|
};
|
|
|
|
const buildCustomVariableEntries = (target: VariableTargetType, definitions: IVariableDefinition[]): IWiredVariablePickerEntry[] =>
|
|
{
|
|
return [ ...definitions ]
|
|
.sort((left, right) => left.name.localeCompare(right.name, undefined, { sensitivity: 'base' }) || (left.itemId - right.itemId))
|
|
.map(definition => ({
|
|
id: `custom:${ definition.itemId }`,
|
|
token: `custom:${ definition.itemId }`,
|
|
label: definition.name,
|
|
displayLabel: definition.name,
|
|
searchableText: definition.name,
|
|
selectable: true,
|
|
hasValue: !!definition.hasValue,
|
|
kind: 'custom',
|
|
target
|
|
}));
|
|
};
|
|
|
|
const getSourceTitle = () => LocalizeText('wiredfurni.params.sources.merged.title.variables');
|
|
|
|
export const WiredConditionVariableAgeMatchView: FC<{}> = () =>
|
|
{
|
|
const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null } = useWired();
|
|
const { userVariableDefinitions = [], furniVariableDefinitions = [], roomVariableDefinitions = [], contextVariableDefinitions = [] } = useWiredTools();
|
|
const [ targetType, setTargetType ] = useState<VariableTargetType>('user');
|
|
const [ variableToken, setVariableToken ] = useState('');
|
|
const [ compareValue, setCompareValue ] = useState(COMPARE_VALUE_CREATED);
|
|
const [ comparison, setComparison ] = useState(COMPARISON_LOWER_THAN);
|
|
const [ durationInput, setDurationInput ] = useState('0');
|
|
const [ durationUnit, setDurationUnit ] = useState(1);
|
|
const [ userSource, setUserSource ] = useState(SOURCE_TRIGGER);
|
|
const [ furniSource, setFurniSource ] = useState(SOURCE_TRIGGER);
|
|
const [ quantifier, setQuantifier ] = useState(QUANTIFIER_ALL);
|
|
|
|
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, roomVariableDefinitions, contextVariableDefinitions), [ contextVariableDefinitions, furniVariableDefinitions, roomVariableDefinitions, targetType, userVariableDefinitions ]);
|
|
const variableEntries = useMemo(() => buildCustomVariableEntries(targetType, 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 'global': return GLOBAL_SOURCE_OPTIONS;
|
|
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) ? intData[0] : TARGET_USER);
|
|
|
|
setTargetType(nextTargetType);
|
|
setVariableToken(normalizeVariableTokenFromWire(trigger.stringData || ''));
|
|
setCompareValue(((intData.length > 1) && (intData[1] === COMPARE_VALUE_UPDATED)) ? COMPARE_VALUE_UPDATED : COMPARE_VALUE_CREATED);
|
|
setComparison(((intData.length > 2) && (intData[2] === COMPARISON_HIGHER_THAN)) ? COMPARISON_HIGHER_THAN : COMPARISON_LOWER_THAN);
|
|
setDurationInput(String((intData.length > 3) ? intData[3] : 0));
|
|
setDurationUnit((intData.length > 4) ? intData[4] : 1);
|
|
setUserSource((intData.length > 5) ? intData[5] : SOURCE_TRIGGER);
|
|
setFurniSource((intData.length > 6) ? intData[6] : (((trigger.selectedItems?.length ?? 0) > 0) ? SOURCE_SELECTED : SOURCE_TRIGGER));
|
|
setQuantifier(((intData.length > 7) && (intData[7] === QUANTIFIER_ANY)) ? QUANTIFIER_ANY : QUANTIFIER_ALL);
|
|
}, [ trigger ]);
|
|
|
|
useEffect(() =>
|
|
{
|
|
if(targetType !== 'user') return;
|
|
if(orderedUserSources.some(option => (option.value === userSource))) return;
|
|
|
|
setUserSource(SOURCE_TRIGGER);
|
|
}, [ orderedUserSources, targetType, userSource ]);
|
|
|
|
useEffect(() =>
|
|
{
|
|
if(targetType !== 'global') return;
|
|
if(compareValue === COMPARE_VALUE_UPDATED) return;
|
|
|
|
setCompareValue(COMPARE_VALUE_UPDATED);
|
|
}, [ targetType, compareValue ]);
|
|
|
|
const save = () =>
|
|
{
|
|
const parsedDuration = parseInt(durationInput.trim(), 10);
|
|
|
|
setStringParam(variableToken);
|
|
setIntParams([
|
|
getTargetValue(targetType),
|
|
compareValue,
|
|
comparison,
|
|
Number.isFinite(parsedDuration) ? Math.max(0, parsedDuration) : 0,
|
|
durationUnit,
|
|
userSource,
|
|
furniSource,
|
|
quantifier
|
|
]);
|
|
|
|
if(requiresFurni <= WiredFurniType.STUFF_SELECTION_OPTION_NONE) setFurniIds([]);
|
|
};
|
|
|
|
const validate = () =>
|
|
{
|
|
if(!variableToken.length) return false;
|
|
if((targetType === 'global') && (compareValue !== COMPARE_VALUE_UPDATED)) return false;
|
|
|
|
return true;
|
|
};
|
|
|
|
const handleTargetChange = (nextTargetType: VariableTargetType) =>
|
|
{
|
|
if(nextTargetType === targetType) return;
|
|
|
|
setTargetType(nextTargetType);
|
|
setVariableToken('');
|
|
};
|
|
|
|
return (
|
|
<WiredConditionBaseView
|
|
hasSpecialInput={ true }
|
|
requiresFurni={ requiresFurni }
|
|
save={ save }
|
|
validate={ validate }
|
|
cardStyle={ { width: 244 } }
|
|
footerCollapsible={ false }
|
|
footer={ (
|
|
<div className="flex flex-col gap-2">
|
|
<div className="flex flex-col gap-1">
|
|
<Text bold>{ LocalizeText('wiredfurni.params.quantifier_selection') }</Text>
|
|
{ [ QUANTIFIER_ALL, QUANTIFIER_ANY ].map(value => (
|
|
<label key={ value } className="flex items-center gap-1">
|
|
<input checked={ (quantifier === value) } className="form-check-input" name="wiredConditionVariableAgeQuantifier" type="radio" onChange={ () => setQuantifier(value) } />
|
|
<Text>{ LocalizeText(`wiredfurni.params.quantifier.variables.${ value }`) }</Text>
|
|
</label>
|
|
)) }
|
|
</div>
|
|
<WiredFurniSelectionSourceRow
|
|
title={ getSourceTitle() }
|
|
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);
|
|
}
|
|
} } />
|
|
</div>
|
|
) }>
|
|
<div className="nitro-wired__give-var">
|
|
<div className="nitro-wired__give-var-heading">
|
|
<Text>{ LocalizeText('wiredfurni.params.variables.variable_selection') }</Text>
|
|
<div className="nitro-wired__give-var-targets">
|
|
{ TARGET_BUTTONS.map(button => (
|
|
<button
|
|
key={ button.key }
|
|
type="button"
|
|
disabled={ button.disabled }
|
|
className={ `nitro-wired__give-var-target nitro-wired__give-var-target--${ button.key } ${ targetType === button.key ? 'is-active' : '' }` }
|
|
onClick={ () => handleTargetChange(button.key) }>
|
|
<img src={ button.icon } alt={ button.key } />
|
|
</button>
|
|
)) }
|
|
</div>
|
|
</div>
|
|
|
|
<WiredVariablePicker
|
|
entries={ resolvedVariableEntries }
|
|
recentScope="variable-conditions"
|
|
selectedToken={ variableToken }
|
|
onSelect={ entry => setVariableToken(entry.token) } />
|
|
|
|
<div className="nitro-wired__divider" />
|
|
|
|
<div className="nitro-wired__give-var-section">
|
|
<div className="nitro-wired__give-var-section-title">{ LocalizeText('wiredfurni.params.variables.compare_value') }</div>
|
|
{ COMPARE_VALUE_OPTIONS.map(value => (
|
|
<label key={ value } className="flex items-center gap-1">
|
|
<input
|
|
checked={ compareValue === value }
|
|
className="form-check-input"
|
|
disabled={ (targetType === 'global') && (value === COMPARE_VALUE_CREATED) }
|
|
name="wiredConditionVariableAgeCompareValue"
|
|
type="radio"
|
|
onChange={ () => setCompareValue(value) } />
|
|
<Text>{ LocalizeText(`wiredfurni.params.variables.compare_value.${ value }`) }</Text>
|
|
</label>
|
|
)) }
|
|
</div>
|
|
|
|
<div className="nitro-wired__divider" />
|
|
|
|
<div className="nitro-wired__give-var-section">
|
|
<div className="nitro-wired__give-var-section-title">{ LocalizeText('wiredfurni.params.choose_type') }</div>
|
|
{ COMPARISON_OPTIONS.map(value => (
|
|
<label key={ value } className="flex items-center gap-1">
|
|
<input checked={ comparison === value } className="form-check-input" name="wiredConditionVariableAgeComparison" type="radio" onChange={ () => setComparison(value) } />
|
|
<Text>{ LocalizeText(`wiredfurni.params.comparison.${ value }`) }</Text>
|
|
</label>
|
|
)) }
|
|
</div>
|
|
|
|
<div className="nitro-wired__divider" />
|
|
|
|
<div className="nitro-wired__give-var-section">
|
|
<div className="nitro-wired__give-var-section-title">{ LocalizeText('wiredfurni.params.variables.time_selection') }</div>
|
|
<div className="flex items-center gap-2">
|
|
<Text>{ LocalizeText('wiredfurni.params.variables.duration') }</Text>
|
|
<NitroInput className="nitro-wired__give-var-number" type="number" value={ durationInput } onChange={ event => setDurationInput(event.target.value) } />
|
|
<select
|
|
className="min-w-0 flex-1 rounded border border-[#b8b2a4] bg-white px-2 py-[3px] text-[12px]"
|
|
value={ durationUnit }
|
|
onChange={ event => setDurationUnit(Number(event.target.value)) }>
|
|
{ DURATION_UNITS.map(unit => (
|
|
<option key={ unit } value={ unit }>
|
|
{ LocalizeText(`wiredfurni.params.variables.duration.${ unit }`) }
|
|
</option>
|
|
)) }
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</WiredConditionBaseView>
|
|
);
|
|
};
|