File "SelectAnimation.jsx"

Full Path: /home/shadsolw/public_html/wp-content/plugins/extendify/src/Agent/workflows/theme/components/SelectAnimation.jsx
File size: 6.97 KB
MIME-type: text/x-java
Charset: utf-8

import { getOption } from '@agent/lib/wp';
import {
	__experimentalToggleGroupControl as ToggleGroupControl,
	__experimentalToggleGroupControlOption as ToggleGroupControlOption,
	Tooltip,
} from '@wordpress/components';
import { useEffect, useRef, useState } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import {
	flipVertical,
	Icon,
	notAllowed,
	shadow,
	square,
} from '@wordpress/icons';
import classNames from 'classnames';

const animations = [
	{
		// translators: Animation style where the element gradually appears by transitioning from transparent to visible. Translate as a simple, non-technical description of the visual effect (e.g., 'appear', 'show up') suitable for non-technical users.
		name: _x('Fade In', 'animation type', 'extendify-local'),
		slug: 'fade',
		icon: shadow,
	},
	{
		// translators: Animation style where the element appears while moving upward. Translate as a simple, non-technical description of the motion (e.g., 'appear from below', 'rise into view') suitable for non-technical users.
		name: _x('Fade Up', 'animation type', 'extendify-local'),
		slug: 'fade-up',
		icon: flipVertical,
	},
	{
		// translators: Animation style where the element appears by scaling from small to its full size. Translate as a simple, non-technical description of the effect (e.g., 'zoom in', 'get closer') suitable for non-technical users. If the target language commonly uses the English term 'zoom', it's acceptable to keep it.
		name: _x('Zoom In', 'animation type', 'extendify-local'),
		slug: 'zoom-in',
		icon: square,
	},
	{
		// translators: None refers to 'no animation'. Apply the correct grammatical gender/form that agrees with the word for 'animation' in the target language.
		name: _x('None', 'animation type', 'extendify-local'),
		slug: 'none',
		icon: notAllowed,
	},
];

const speeds = [
	{
		// translators: "Slow" refers to a slower speed at which the animation effect occurs. This is an option in a list of animation speeds.
		name: _x('Slow', 'animation speed', 'extendify-local'),
		slug: 'slow',
	},
	{
		// translators: "Medium" refers to a moderate speed at which the animation effect occurs. This is an option in a list of animation speeds.
		name: _x('Medium', 'animation speed', 'extendify-local'),
		slug: 'medium',
	},
	{
		// translators: "Fast" refers to a quicker speed at which the animation effect occurs. This is an option in a list of animation speeds.
		name: _x('Fast', 'animation speed', 'extendify-local'),
		slug: 'fast',
	},
];

export const SelectAnimation = ({ onConfirm, onCancel }) => {
	const initialSettings = useRef({});
	const [animation, setAnimation] = useState(null);
	const [touched, setTouched] = useState(0);
	const [speed, setSpeed] = useState(null);
	const [loading, setLoading] = useState(true);

	const handleConfirm = () => {
		if (!animation || !speed || loading) return;
		onConfirm({ data: { type: animation, speed } });
	};

	const handleCancel = () => {
		if (!touched) return onCancel();
		window.ExtendableAnimations.setType(initialSettings.current.type);
		window.ExtendableAnimations.setSpeed(initialSettings.current.speed);
		onCancel();
	};

	useEffect(() => {
		if (!touched) return;
		window.ExtendableAnimations.setType(animation);
		window.ExtendableAnimations.setSpeed(speed);
	}, [speed, animation, touched]);

	useEffect(() => {
		if (!loading) return;
		getOption('extendify_animation_settings').then((settings = {}) => {
			const defaults = { type: 'none', speed: 'medium' };
			initialSettings.current = { ...defaults, ...(settings || {}) };
			setAnimation(initialSettings.current.type);
			setSpeed(initialSettings.current.speed);
			setLoading(false);
		});
	}, []);

	if (loading) {
		return (
			<div className="min-h-24 p-2 text-center text-sm">
				{
					// translators: This is a status message. 'Loading' is a verb — the app is currently loading the animation options. It is not referring to 'loading animation' as a compound noun.
					__('Loading animation options...', 'extendify-local')
				}
			</div>
		);
	}

	return (
		<div className="mb-4 ml-10 mr-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50 rtl:ml-2 rtl:mr-10">
			<div className="rounded-lg p-3 border-b border-gray-300 bg-white">
				<div className="text-xs uppercase mb-3 text-gray-700 font-medium">
					{/* translators: "Type" refers to the category of animation effects available. e.g. The type could be 'Zoom In', 'Fade', etc. */}
					{_x('Type', 'animation type', 'extendify-local')}
				</div>
				<div
					className="grid gap-2 mb-6"
					style={{
						gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))',
						gridAutoRows: '1fr',
					}}
				>
					{animations.map(({ name, slug, icon }) => (
						<Tooltip key={slug} text={name} placement="top">
							<div>
								<Button
									name={name}
									icon={icon}
									selected={animation === slug}
									onClick={() => {
										setTouched((v) => v + 1);
										setAnimation(slug);
									}}
								/>
							</div>
						</Tooltip>
					))}
				</div>
				{/* mb-1 because the toggle group has margins even if undefined */}
				<div className="text-xs uppercase mb-1 text-gray-700 font-medium">
					{/* translators: "Speed" refers to the speed at which the animation effect occurs. This is a heading label shown above the toggle group for selecting animation speed. */}
					{_x('Speed', 'animation speed', 'extendify-local')}
				</div>
				<ToggleGroupControl
					className="border-gray-300 rounded-sm m-0 before:rounded-sm focus-within:border-gray-900"
					__next40pxDefaultSize
					__nextHasNoMarginBottom
					isBlock
					value={speed}
					onChange={(speed) => {
						setTouched((v) => v + 1);
						setSpeed(speed);
					}}
				>
					{speeds.map(({ name, slug }) => (
						<ToggleGroupControlOption key={slug} label={name} value={slug} />
					))}
				</ToggleGroupControl>
			</div>
			<div className="flex justify-start gap-2 p-3">
				<button
					type="button"
					className="w-full rounded-sm border border-gray-500 bg-white p-2 text-sm text-gray-900"
					onClick={handleCancel}
				>
					{__('Cancel', 'extendify-local')}
				</button>
				<button
					type="button"
					className="w-full rounded-sm border border-design-main bg-design-main p-2 text-sm text-white"
					disabled={!animation || !speed || loading}
					onClick={handleConfirm}
				>
					{__('Save', 'extendify-local')}
				</button>
			</div>
		</div>
	);
};

const Button = ({ name, selected, icon, onClick }) => {
	return (
		<button
			type="button"
			className={classNames(
				'relative h-full w-full rounded-lg border p-2 text-sm text-left flex flex-col gap-4 font-medium hover:bg-gray-100',
				{
					'border-gray-900 bg-gray-100': selected,
					'border-gray-300 bg-gray-50': !selected,
				},
			)}
			onClick={onClick}
		>
			<Icon icon={icon} className="h-6 w-6" />
			{name}
			{selected && (
				<div className="absolute top-1 right-1 h-3.5 w-3.5 flex items-center justify-center rounded-full bg-design-main text-center text-xss text-white leading-none">
					<span>✓</span>
				</div>
			)}
		</button>
	);
};