5180 fichiers 38.5 Mo 0 téléchargements 377 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / wired / views / actions / WiredActionChangeOpacityView.tsx
WiredActionChangeOpacityView.tsx 10.3 Ko Télécharger le fichier
import { FC, useEffect, useRef, useState } from 'react';
import { localizeWithFallback, WiredFurniType } from '../../../../api';
import { Slider, Text } from '../../../../common';
import { useNotification, useWired } from '../../../../hooks';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { WiredActionBaseView } from './WiredActionBaseView';
import {
    normalizeWiredActionChangeOpacity,
    serializeWiredActionChangeOpacity,
    WIRED_OPACITY_DEFAULT_DURATION,
    WIRED_OPACITY_EASING_INSTANT,
    WIRED_OPACITY_MAXIMUM,
    WIRED_OPACITY_MAXIMUM_DURATION,
    WIRED_OPACITY_MINIMUM,
    WIRED_OPACITY_SELECTED_FURNI_SOURCE,
    WIRED_OPACITY_VISIBILITY_EVERYONE,
    WIRED_OPACITY_VISIBILITY_SOURCE_USERS,
    WiredActionChangeOpacityState
} from './WiredActionChangeOpacityState';
import { Checkbox } from '../../../../common/Checkbox';
import { Radio } from '../../../../common/Radio';
import { Select } from '../../../../common/Select';
import { NumberInput } from '../../../../common/NumberInput';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';

const EASING_FALLBACKS = ['Instant', 'Linear', 'Ease in', 'Ease out', 'Ease in and out'];

type VisibilityChoice = 'visible' | 'invisible' | 'custom';

const visibilityChoice = (opacity: number): VisibilityChoice =>
    opacity >= WIRED_OPACITY_MAXIMUM ? 'visible' : opacity <= WIRED_OPACITY_MINIMUM ? 'invisible' : 'custom';

/**
 * Official set furni transparency (61): audience, visible / invisible, click handling and easing.
 * Ours on top: an in-between transparency and the transition seconds.
 */
export const WiredActionChangeOpacityView: FC<{}> = () => {
    const { trigger = null, setFurniIds = null, setIntParams = null, setSourceTypes = null } = useWired();
    const { simpleAlert = null } = useNotification();
    const read = () => normalizeWiredActionChangeOpacity(trigger?.intData, trigger?.furniSourceTypes, trigger?.userSourceTypes);
    const [state, setState] = useState<WiredActionChangeOpacityState>(read);
    const everyoneInfoShown = useRef(false);

    useEffect(() => {
        setState(read());
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [trigger]);

    const update = (patch: Partial<WiredActionChangeOpacityState>) => setState((current) => ({ ...current, ...patch }));

    const changeAudience = (visibility: number) => {
        // Official: choosing "everyone" the first time explains how to keep it after re-entering.
        if (visibility === WIRED_OPACITY_VISIBILITY_EVERYONE && !everyoneInfoShown.current && simpleAlert) {
            everyoneInfoShown.current = true;
            simpleAlert(
                localizeWithFallback('wiredfurni.params.furni_visualization.audience.everyone_info.body', 'Stack this effect with "When a user enters the room" to keep it after re-entering.'),
                null,
                null,
                null,
                localizeWithFallback('wiredfurni.params.furni_visualization.audience.everyone_info.title', 'Keeping this effect visible after re-entering')
            );
        }

        update({ visibility });
    };

    const changeVisibility = (choice: VisibilityChoice) => {
        if (choice === 'visible') update({ opacity: WIRED_OPACITY_MAXIMUM });
        else if (choice === 'invisible') update({ opacity: WIRED_OPACITY_MINIMUM });
        else update({ opacity: 50 });
    };

    const save = () => {
        const wire = serializeWiredActionChangeOpacity(state);

        setIntParams(wire.intParams);
        setSourceTypes({ furni: wire.furniSources, users: wire.userSources });
        if (state.targetUsers || state.furniSource !== WIRED_OPACITY_SELECTED_FURNI_SOURCE) setFurniIds?.([]);
    };

    const choice = visibilityChoice(state.opacity);
    const radio = (value: VisibilityChoice, key: string, fallback: string) => (
        <label className="flex items-center gap-1">
            <Radio checked={choice === value} name="wiredTransparency" onCheckedChange={() => changeVisibility(value)} />
            <Text>{localizeWithFallback(key, fallback)}</Text>
        </label>
    );

    return (
        <NativeTextTree>
            <WiredActionBaseView
                hasSpecialInput={true}
                requiresFurni={state.targetUsers ? WiredFurniType.STUFF_SELECTION_OPTION_NONE : WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT}
                save={save}
                footerCollapsible={false}
                footer={
                    <div className="flex flex-col gap-2">
                        <Text bold>{localizeWithFallback('wiredfurni.params.sources.merged.title.target', 'Select target:')}</Text>
                        <div className="flex gap-3">
                            <label className="flex items-center gap-1">
                                <Radio checked={!state.targetUsers} name="wiredTransparencyTarget" onCheckedChange={() => update({ targetUsers: false })} />
                                <Text>{localizeWithFallback('wiredfurni.params.sources.furni.title', 'Furni')}</Text>
                            </label>
                            <label className="flex items-center gap-1">
                                <Radio checked={state.targetUsers} name="wiredTransparencyTarget" onCheckedChange={() => update({ targetUsers: true })} />
                                <Text>{localizeWithFallback('wiredfurni.params.sources.users.title', 'Users')}</Text>
                            </label>
                        </div>
                        {state.targetUsers ? (
                            <WiredSourcesSelector showUsers={true} userSlot={1} userSource={state.targetUserSource} onChangeUsers={(targetUserSource) => update({ targetUserSource })} />
                        ) : (
                            <WiredSourcesSelector showFurni={true} furniSource={state.furniSource} onChangeFurni={(furniSource) => update({ furniSource })} />
                        )}
                        {state.visibility === WIRED_OPACITY_VISIBILITY_SOURCE_USERS && (
                            <WiredSourcesSelector
                                showUsers={true}
                                usersTitle="wiredfurni.params.visualization.visibility_audience"
                                userSource={state.userSource}
                                onChangeUsers={(userSource) => update({ userSource })}
                            />
                        )}
                    </div>
                }
            >
                <div className="flex flex-col gap-1">
                    <Text bold>{localizeWithFallback('wiredfurni.params.furni_visualization.audience', 'Who should see the effect?')}</Text>
                    <Select className="form-select form-select-sm" value={state.visibility} onChange={(event) => changeAudience(Number(event.target.value))}>
                        <option value={WIRED_OPACITY_VISIBILITY_SOURCE_USERS}>{localizeWithFallback('wiredfurni.params.furni_visualization.audience.selected_users', 'Selected users')}</option>
                        <option value={WIRED_OPACITY_VISIBILITY_EVERYONE}>{localizeWithFallback('wiredfurni.params.furni_visualization.audience.everyone', 'Everyone, also users who enter later')}</option>
                    </Select>
                </div>

                <div className="flex flex-col gap-1">
                    <Text bold>{localizeWithFallback('wiredfurni.params.set_furni_transparency.visibility', 'Visibility:')}</Text>
                    {radio('visible', 'wiredfurni.params.set_furni_transparency.visible', 'Visible')}
                    {radio('invisible', 'wiredfurni.params.set_furni_transparency.invisible', 'Invisible')}
                    {radio('custom', 'wiredfurni.params.opacity.visibility_amount', 'Opacity')}
                    {choice === 'custom' && (
                        <div className="flex items-center gap-2">
                            <Slider
                                max={WIRED_OPACITY_MAXIMUM - 1}
                                min={WIRED_OPACITY_MINIMUM + 1}
                                step={1}
                                value={state.opacity}
                                onChange={(opacity) => update({ opacity: opacity as number })}
                            />
                            <Text small>{state.opacity}%</Text>
                        </div>
                    )}
                </div>

                <div className="flex flex-col gap-1">
                    <Text bold>{localizeWithFallback('wiredfurni.params.set_furni_transparency.click_handling', 'Click handling:')}</Text>
                    <label className="flex items-center gap-1">
                        <Checkbox checked={state.clickThrough} onCheckedChange={(nextChecked) => update({ clickThrough: nextChecked })} />
                        <Text>{localizeWithFallback('wiredfurni.params.set_furni_transparency.allow_click_through', 'The target cannot be clicked')}</Text>
                    </label>
                </div>

                <div className="flex flex-col gap-1">
                    <Text bold>{localizeWithFallback('wiredfurni.params.set_furni_transparency.easing', 'Transition:')}</Text>
                    <Select className="form-select form-select-sm" value={state.easing} onChange={(event) => update({ easing: Number(event.target.value) })}>
                        {EASING_FALLBACKS.map((fallback, value) => (
                            <option key={value} value={value}>
                                {localizeWithFallback(`wiredfurni.params.set_furni_transparency.easing.${value}`, fallback)}
                            </option>
                        ))}
                    </Select>
                    {state.easing !== WIRED_OPACITY_EASING_INSTANT && (
                        <label className="flex items-center justify-between gap-2">
                            <Text>{localizeWithFallback('wiredfurni.params.variables.duration', 'Duration (seconds, 0 = default)')}</Text>
                            <NumberInput aria-label="Transition duration in seconds, 0 uses the default transition" className="form-control form-control-sm max-w-[80px]" max={WIRED_OPACITY_MAXIMUM_DURATION} min={WIRED_OPACITY_DEFAULT_DURATION} value={state.duration} onChange={(event) => update({ duration: Number(event.target.value) })} />
                        </label>
                    )}
                </div>
            </WiredActionBaseView>
        </NativeTextTree>
    );
};
15Sujets
1Dépôts
20Membres
3En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser