PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / 3.3.2
Block Animations, Motion & Scroll Effects – Ghost Kit v3.3.2
3.7.2 3.7.1 3.7.0 3.6.1 trunk 1.6.3 2.25.0 3.3.0 3.3.1 3.3.2 3.3.3 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.6.0
ghostkit / gutenberg / components / transition-easing-controls / index.js

index.js in Block Animations, Motion & Scroll Effects – Ghost Kit 3.3.2, at gutenberg/components/transition-easing-controls/index.js

310 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // TODO: check for better implementation - https://codepen.io/osublake/pen/OyPGEo
2 import BezierEditor from 'bezier-easing-editor';
3 import classnames from 'classnames/dedupe';
4
5 import {
6 __experimentalNumberControl as ExperimentalNumberControl,
7 __stableNumberControl as StableNumberControl,
8 BaseControl,
9 } from '@wordpress/components';
10 import { useEffect, useState } from '@wordpress/element';
11 import { __ } from '@wordpress/i18n';
12
13 import round from '../../utils/round';
14 import Select from '../select';
15 import TransitionPreview from '../transition-preview';
16 import DEFAULT from './default';
17 import PRESETS from './presets';
18
19 const NumberControl = StableNumberControl || ExperimentalNumberControl;
20
21 export { DEFAULT };
22 export { PRESETS };
23
24 export function EasingBezierEditor(props) {
25 const { value, onChange, variant = '', backgroundColor } = props;
26
27 let options = {
28 value,
29 width: 228,
30 height: 140,
31 padding: [25, 55, 25, 55],
32 background: 'transparent',
33 color: '#ccc',
34 gridColor: 'transparent',
35 curveColor: '#bfbfbf',
36 progressColor: '#ccc',
37 handleColor: 'var(--wp-admin-theme-color)',
38 curveWidth: 2,
39 handleRadius: 6,
40 handleStroke: 2,
41 textStyle: {
42 display: 'none',
43 },
44 // We have to add the onChange callback to prevent warning in console.
45 onChange(val) {
46 if (onChange) {
47 onChange(val);
48 }
49 },
50 };
51
52 if (variant === 'preview') {
53 options = {
54 ...options,
55 readOnly: true,
56 width: 20,
57 height: 20,
58 padding: [3, 3, 3, 3],
59 curveColor: '#fff',
60 curveWidth: 1.5,
61 };
62 }
63
64 return (
65 <div
66 className={classnames(
67 'ghostkit-component-bezier-editor',
68 variant && `ghostkit-component-bezier-editor-${variant}`
69 )}
70 style={backgroundColor ? { backgroundColor } : {}}
71 >
72 <BezierEditor {...options} />
73 </div>
74 );
75 }
76
77 export function EasingControls(props) {
78 const { value, onChange, enableDelayControl = true } = props;
79
80 const [preset, setPreset] = useState();
81
82 function updateValue(val) {
83 // Fix invalid easing value
84 if (val.easing && val.easing.length) {
85 val.easing[0] = Math.max(Math.min(val.easing[0], 1), 0);
86 val.easing[2] = Math.max(Math.min(val.easing[2], 1), 0);
87 }
88
89 onChange({ ...value, ...val });
90 }
91
92 let easing = value?.easing;
93 if (!easing || easing.length !== 4) {
94 easing = DEFAULT.easing;
95 }
96
97 // Find default preset
98 useEffect(() => {
99 let newPreset = 'custom';
100
101 Object.keys(PRESETS).forEach((slug) => {
102 const presetData = PRESETS[slug].easing;
103
104 if (JSON.stringify(easing) === JSON.stringify(presetData)) {
105 newPreset = slug;
106 }
107 });
108
109 setPreset(newPreset);
110 }, [preset, easing]);
111
112 const presetOptions = [
113 ...(preset === 'custom'
114 ? [
115 {
116 value: 'custom',
117 label: __('-- Presets --', 'ghostkit'),
118 },
119 ]
120 : []),
121 ...Object.keys(PRESETS).map((name) => {
122 return {
123 value: name,
124 label: PRESETS[name].label,
125 };
126 }),
127 ];
128
129 const presetValue = {
130 value: preset,
131 label: preset,
132 };
133
134 // Find actual label.
135 if (presetValue.value) {
136 presetOptions.forEach((presetData) => {
137 if (presetValue.value === presetData.value) {
138 presetValue.label = presetData.label;
139 }
140 });
141 }
142
143 // Parse pasted cubic bezier values.
144 // Supported values:
145 // - cubic-bezier(.62,1.45,0,-0.7)
146 // - (.62,1.45,0,-0.7)
147 // - .62,1.45,0,-0.7
148 // - 0.62,1.45,0,-0.7
149 // - 0.62, 1.45, 0, -0.7
150 function handlePasteBezier(e) {
151 const clipboard = e.clipboardData.getData('text');
152
153 if (!clipboard) {
154 return;
155 }
156
157 const parts = clipboard.split(',');
158
159 if (!parts || parts.length !== 4) {
160 return;
161 }
162
163 parts[0] = parts[0].replace(/^cubic-bezier/, '').replace(/^\(/, '');
164 parts[3] = parts[3].replace(/\)$/, '');
165
166 const result = parts.map((a) => {
167 return parseFloat(a);
168 });
169
170 if (!result || result.length !== 4) {
171 return;
172 }
173
174 e.preventDefault();
175
176 updateValue({ easing: result });
177 }
178
179 return (
180 <>
181 <Select
182 value={presetValue}
183 onChange={({ value: newPreset }) => {
184 if (PRESETS?.[newPreset]?.easing) {
185 updateValue({ easing: PRESETS[newPreset].easing });
186 }
187 }}
188 options={presetOptions}
189 isSearchable={false}
190 />
191 <EasingBezierEditor
192 value={easing}
193 onChange={(val) => {
194 const newEase = val.map((v) => {
195 return round(v, 2);
196 });
197
198 updateValue({ easing: newEase });
199 }}
200 />
201 <BaseControl
202 id={__('Bezier', 'ghostkit')}
203 label={__('Bezier', 'ghostkit')}
204 className="ghostkit-component-easing-controls-bezier"
205 >
206 <NumberControl
207 value={easing[0]}
208 onChange={(val) =>
209 updateValue({
210 easing: [
211 round(parseFloat(val), 2),
212 easing[1],
213 easing[2],
214 easing[3],
215 ],
216 })
217 }
218 onPaste={(e) => handlePasteBezier(e)}
219 min={0}
220 max={1}
221 step={0.01}
222 />
223 <NumberControl
224 value={easing[1]}
225 onChange={(val) =>
226 updateValue({
227 easing: [
228 easing[0],
229 round(parseFloat(val), 2),
230 easing[2],
231 easing[3],
232 ],
233 })
234 }
235 onPaste={(e) => handlePasteBezier(e)}
236 min={-5}
237 max={5}
238 step={0.01}
239 />
240 <NumberControl
241 value={easing[2]}
242 onChange={(val) =>
243 updateValue({
244 easing: [
245 easing[0],
246 easing[1],
247 round(parseFloat(val), 2),
248 easing[3],
249 ],
250 })
251 }
252 onPaste={(e) => handlePasteBezier(e)}
253 min={0}
254 max={1}
255 step={0.01}
256 />
257 <NumberControl
258 value={easing[3]}
259 onChange={(val) =>
260 updateValue({
261 easing: [
262 easing[0],
263 easing[1],
264 easing[2],
265 round(parseFloat(val), 2),
266 ],
267 })
268 }
269 onPaste={(e) => handlePasteBezier(e)}
270 min={-5}
271 max={5}
272 step={0.01}
273 />
274 </BaseControl>
275 <NumberControl
276 label={__('Duration', 'ghostkit')}
277 suffix="s&nbsp;"
278 value={value?.duration || 0}
279 onChange={(val) => updateValue({ duration: parseFloat(val) })}
280 labelPosition="edge"
281 __unstableInputWidth="90px"
282 min={0}
283 max={10}
284 step={0.01}
285 />
286 {enableDelayControl && (
287 <NumberControl
288 label={__('Delay', 'ghostkit')}
289 suffix="s&nbsp;"
290 value={value?.delay || 0}
291 onChange={(val) => updateValue({ delay: parseFloat(val) })}
292 labelPosition="edge"
293 __unstableInputWidth="90px"
294 min={0}
295 max={10}
296 step={0.01}
297 />
298 )}
299 <TransitionPreview
300 label={__('Preview', 'ghostkit')}
301 options={{
302 type: 'easing',
303 duration: value?.duration || 0,
304 easing,
305 }}
306 />
307 </>
308 );
309 }
310