PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Agent / workflows / theme / components / SelectAnimation.jsx

SelectAnimation.jsx in Extendify 3.2.1, at src/Agent/workflows/theme/components/SelectAnimation.jsx

204 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { getOption } from '@agent/lib/wp';
2 import {
3 __experimentalToggleGroupControl as ToggleGroupControl,
4 __experimentalToggleGroupControlOption as ToggleGroupControlOption,
5 } from '@wordpress/components';
6 import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
7 import { __, _x } from '@wordpress/i18n';
8 import {
9 flipVertical,
10 Icon,
11 notAllowed,
12 shadow,
13 square,
14 } from '@wordpress/icons';
15 import classNames from 'classnames';
16
17 const animations = [
18 {
19 // 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.
20 name: _x('Fade In', 'animation type', 'extendify-local'),
21 slug: 'fade',
22 icon: shadow,
23 },
24 {
25 // 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.
26 name: _x('Fade Up', 'animation type', 'extendify-local'),
27 slug: 'fade-up',
28 icon: flipVertical,
29 },
30 {
31 // 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.
32 name: _x('Zoom In', 'animation type', 'extendify-local'),
33 slug: 'zoom-in',
34 icon: square,
35 },
36 {
37 // translators: None refers to 'no animation'. Apply the correct grammatical gender/form that agrees with the word for 'animation' in the target language.
38 name: _x('None', 'animation type', 'extendify-local'),
39 slug: 'none',
40 icon: notAllowed,
41 },
42 ];
43
44 const speeds = [
45 {
46 // translators: "Slow" refers to a slower speed at which the animation effect occurs. This is an option in a list of animation speeds.
47 name: _x('Slow', 'animation speed', 'extendify-local'),
48 slug: 'slow',
49 },
50 {
51 // translators: "Medium" refers to a moderate speed at which the animation effect occurs. This is an option in a list of animation speeds.
52 name: _x('Medium', 'animation speed', 'extendify-local'),
53 slug: 'medium',
54 },
55 {
56 // translators: "Fast" refers to a quicker speed at which the animation effect occurs. This is an option in a list of animation speeds.
57 name: _x('Fast', 'animation speed', 'extendify-local'),
58 slug: 'fast',
59 },
60 ];
61
62 export const SelectAnimation = ({ onConfirm, onCancel }) => {
63 const initialSettings = useRef({});
64 const [animation, setAnimation] = useState(null);
65 const [touched, setTouched] = useState(0);
66 const [speed, setSpeed] = useState(null);
67 const [loading, setLoading] = useState(true);
68 const undoAnimation = useCallback(() => {
69 if (!touched) return;
70 window.ExtendableAnimations?.setType(initialSettings.current.type);
71 window.ExtendableAnimations?.setSpeed(initialSettings.current.speed);
72 }, [touched]);
73
74 const confirmed = useRef(false);
75 // Ref keeps the latest undo function, so clean-up always sees the current `touched` state.
76 const undoRef = useRef(undoAnimation);
77 undoRef.current = undoAnimation;
78 useEffect(() => {
79 return () => {
80 if (!confirmed.current) undoRef.current();
81 };
82 }, []);
83
84 const handleConfirm = () => {
85 if (!animation || !speed || loading) return;
86 confirmed.current = true;
87 onConfirm({ data: { type: animation, speed } });
88 };
89
90 useEffect(() => {
91 if (!touched) return;
92 window.ExtendableAnimations.setType(animation);
93 window.ExtendableAnimations.setSpeed(speed);
94 }, [speed, animation, touched]);
95
96 useEffect(() => {
97 if (!loading) return;
98 getOption('extendify_animation_settings').then((settings = {}) => {
99 const defaults = { type: 'none', speed: 'medium' };
100 initialSettings.current = { ...defaults, ...(settings || {}) };
101 setAnimation(initialSettings.current.type);
102 setSpeed(initialSettings.current.speed);
103 setLoading(false);
104 });
105 }, []);
106
107 if (loading) {
108 return (
109 <div className="min-h-24 p-2 text-center text-sm">
110 {
111 // 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.
112 __('Loading animation options...', 'extendify-local')
113 }
114 </div>
115 );
116 }
117
118 return (
119 <div className="mb-4 ms-2 me-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50">
120 <div className="rounded-lg p-3 border-b border-gray-300 bg-white">
121 <div className="text-xs uppercase mb-3 text-gray-700 font-medium">
122 {/* translators: "Type" refers to the category of animation effects available. e.g. The type could be 'Zoom In', 'Fade', etc. */}
123 {_x('Type', 'animation type', 'extendify-local')}
124 </div>
125 <div className="grid gap-2 mb-6 grid-cols-2 auto-rows-fr">
126 {animations.map(({ name, slug, icon }) => (
127 <Button
128 key={slug}
129 name={name}
130 icon={icon}
131 selected={animation === slug}
132 onClick={() => {
133 setTouched((v) => v + 1);
134 setAnimation(slug);
135 }}
136 />
137 ))}
138 </div>
139 {/* mb-1 because the toggle group has margins even if undefined */}
140 <div className="text-xs uppercase mb-1 text-gray-700 font-medium">
141 {/* 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. */}
142 {_x('Speed', 'animation speed', 'extendify-local')}
143 </div>
144 <ToggleGroupControl
145 className="border-gray-300 rounded-sm m-0 before:rounded-sm focus-within:border-gray-900"
146 __next40pxDefaultSize
147 __nextHasNoMarginBottom
148 isBlock
149 value={speed}
150 onChange={(speed) => {
151 setTouched((v) => v + 1);
152 setSpeed(speed);
153 }}
154 >
155 {speeds.map(({ name, slug }) => (
156 <ToggleGroupControlOption key={slug} label={name} value={slug} />
157 ))}
158 </ToggleGroupControl>
159 </div>
160 <div className="flex justify-start gap-2 p-3">
161 <button
162 type="button"
163 className="w-full rounded-sm border border-gray-500 bg-white p-2 text-sm text-gray-900"
164 onClick={onCancel}
165 >
166 {__('Cancel', 'extendify-local')}
167 </button>
168 <button
169 type="button"
170 className="w-full rounded-sm border border-design-main bg-design-main p-2 text-sm text-white"
171 disabled={!animation || !speed || loading}
172 onClick={handleConfirm}
173 >
174 {__('Save', 'extendify-local')}
175 </button>
176 </div>
177 </div>
178 );
179 };
180
181 const Button = ({ name, selected, icon, onClick }) => {
182 return (
183 <button
184 type="button"
185 className={classNames(
186 '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',
187 {
188 'border-gray-900 bg-gray-100': selected,
189 'border-gray-300 bg-gray-50': !selected,
190 },
191 )}
192 onClick={onClick}
193 >
194 <Icon icon={icon} className="h-6 w-6" />
195 {name}
196 {selected && (
197 <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">
198 <span></span>
199 </div>
200 )}
201 </button>
202 );
203 };
204