@secretstache/wordpress-gutenberg 0.5.4 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secretstache/wordpress-gutenberg",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "",
5
5
  "author": "Secret Stache",
6
6
  "license": "GPL-2.0-or-later",
@@ -1,4 +1,4 @@
1
- import { BaseControl, Button, Icon as WPIcon } from '@wordpress/components';
1
+ import { BaseControl, Button, FocalPointPicker, Icon as WPIcon } from '@wordpress/components';
2
2
  import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
3
3
  import { page as pageIcon } from '@wordpress/icons';
4
4
 
@@ -110,41 +110,67 @@ export const AnimationRenderer = ({
110
110
  };
111
111
 
112
112
  export const MediaControl = ({
113
+ type = MEDIA_TYPE.IMAGE,
113
114
  label,
115
+
114
116
  mediaId,
115
117
  mediaUrl,
116
118
  mediaFileName = '',
119
+
117
120
  onSelect,
118
121
  onRemove,
119
- type = MEDIA_TYPE.IMAGE,
122
+
120
123
  selectButtonLabel,
121
124
  removeButtonLabel,
125
+
126
+ hasFocalPoint = true,
127
+ focalPointLabel = 'Focal Point',
128
+ focalPoint = { x: 0.5, y: 0.5 },
129
+ onFocalPointChange,
130
+
122
131
  ...other
123
132
  }) => {
124
133
  if (type === MEDIA_TYPE.IMAGE) {
125
134
  return (
126
- <BaseControl label={label || 'Image'}>
127
- <MediaUploadCheck>
128
- <MediaUpload
129
- onSelect={onSelect}
130
- allowedTypes={['image', 'image/svg+xml']}
131
- accept="image/*"
132
- value={mediaId}
133
- render={({ open }) => (
134
- <ImageRenderer
135
- imageId={mediaId}
136
- imageUrl={mediaUrl}
137
- onImageClick={open}
138
- onSelectClick={open}
139
- onRemoveClick={onRemove}
140
- selectButtonLabel={selectButtonLabel || 'Select Image'}
141
- removeButtonLabel={removeButtonLabel || 'Remove Image'}
135
+ <>
136
+ <BaseControl label={label || 'Image'}>
137
+ <MediaUploadCheck>
138
+ <MediaUpload
139
+ onSelect={onSelect}
140
+ allowedTypes={['image', 'image/svg+xml']}
141
+ accept="image/*"
142
+ value={mediaId}
143
+ render={({ open }) => (
144
+ <ImageRenderer
145
+ imageId={mediaId}
146
+ imageUrl={mediaUrl}
147
+ onImageClick={open}
148
+ onSelectClick={open}
149
+ onRemoveClick={onRemove}
150
+ selectButtonLabel={selectButtonLabel || 'Select Image'}
151
+ removeButtonLabel={removeButtonLabel || 'Remove Image'}
152
+ />
153
+ )}
154
+ {...other}
155
+ />
156
+ </MediaUploadCheck>
157
+ </BaseControl>
158
+
159
+ {
160
+ hasFocalPoint && mediaId && mediaUrl && (
161
+ <BaseControl label={focalPointLabel}>
162
+ <FocalPointPicker
163
+ __nextHasNoMarginBottom
164
+ url={mediaUrl}
165
+ value={focalPoint}
166
+ onDragStart={onFocalPointChange}
167
+ onDrag={onFocalPointChange}
168
+ onChange={onFocalPointChange}
142
169
  />
143
- )}
144
- {...other}
145
- />
146
- </MediaUploadCheck>
147
- </BaseControl>
170
+ </BaseControl>
171
+ )
172
+ }
173
+ </>
148
174
  );
149
175
  } else if (type === MEDIA_TYPE.VIDEO) {
150
176
  return (
@@ -0,0 +1,78 @@
1
+ import { BaseControl, FocalPointPicker } from '@wordpress/components';
2
+ import { useCallback, memo } from '@wordpress/element';
3
+
4
+ import { MediaControl } from './MediaControl.js';
5
+ import { MEDIA_TYPE } from '../utils/index.js';
6
+
7
+ /**
8
+ * Media control with focal point functionality
9
+ *
10
+ * @param {Object} props - Component props
11
+ * @returns {JSX.Element} Component JSX
12
+ */
13
+ export const MediaWithFocalPointControl = memo(({
14
+ attributes,
15
+ setAttributes,
16
+ mediaAttributeName = 'media',
17
+ focalPointAttributeName = 'focalPoint',
18
+ mediaLabel = 'Image',
19
+ focalPointLabel = 'Focal Point',
20
+ mediaControlProps = {},
21
+ }) => {
22
+ const media = attributes[mediaAttributeName];
23
+ const focalPoint = attributes[focalPointAttributeName] || { x: 0.5, y: 0.5 };
24
+
25
+ const onFocalPointChange = useCallback((newFocalPoint) => {
26
+ setAttributes({ [focalPointAttributeName]: newFocalPoint });
27
+ }, [ focalPointAttributeName ]);
28
+
29
+ const onMediaSelect = useCallback((newMedia) => {
30
+ setAttributes({
31
+ [mediaAttributeName]: {
32
+ id: newMedia.id,
33
+ url: newMedia.url,
34
+ alt: newMedia.alt || '',
35
+ },
36
+
37
+ [focalPointAttributeName]: { x: 0.5, y: 0.5 }
38
+ });
39
+ }, [ mediaAttributeName ]);
40
+
41
+ const onMediaRemove = useCallback(() => {
42
+ setAttributes({
43
+ [mediaAttributeName]: {
44
+ id: null,
45
+ url: null,
46
+ alt: null,
47
+ }
48
+ });
49
+ }, [ mediaAttributeName ]);
50
+
51
+ return (
52
+ <>
53
+ <BaseControl label={mediaLabel}>
54
+ <MediaControl
55
+ type={MEDIA_TYPE.IMAGE}
56
+ mediaId={media?.id}
57
+ mediaUrl={media?.url}
58
+ onSelect={onMediaSelect}
59
+ onRemove={onMediaRemove}
60
+ {...mediaControlProps}
61
+ />
62
+ </BaseControl>
63
+
64
+ {media?.url && (
65
+ <BaseControl label={focalPointLabel}>
66
+ <FocalPointPicker
67
+ __nextHasNoMarginBottom
68
+ url={media.url}
69
+ value={focalPoint}
70
+ onDragStart={onFocalPointChange}
71
+ onDrag={onFocalPointChange}
72
+ onChange={onFocalPointChange}
73
+ />
74
+ </BaseControl>
75
+ )}
76
+ </>
77
+ );
78
+ });
@@ -7,9 +7,10 @@ export { SortableSelect, SortableSelectAsync } from './SortableSelect';
7
7
  export { DataQueryControls } from './DataQueryControls.js';
8
8
  export { SpacingControl } from './SpacingControl.js';
9
9
  export { ResponsiveSpacingControl } from './ResponsiveSpacingControl.js';
10
- export { ResourcesWrapper } from './ResourcesWrapper.js'
11
- export { DividersControl } from './DividersControl.js'
12
- export { MediaTypeControl } from './MediaTypeControl.js'
13
- export { InsertBlockToolbar } from './InsertBlockToolbar.js'
14
- export { PreviewControl } from './PreviewControl.js'
15
- export { EmptyBlockAppender } from './EmptyBlockAppender.js'
10
+ export { ResourcesWrapper } from './ResourcesWrapper.js';
11
+ export { DividersControl } from './DividersControl.js';
12
+ export { MediaTypeControl } from './MediaTypeControl.js';
13
+ export { InsertBlockToolbar } from './InsertBlockToolbar.js';
14
+ export { PreviewControl } from './PreviewControl.js';
15
+ export { EmptyBlockAppender } from './EmptyBlockAppender.js';
16
+ export { MediaWithFocalPointControl } from './MediaWithFocalPointControl.js';
@@ -254,3 +254,22 @@ export function updateBlockApiVersion(blockName, apiVersion = 3) {
254
254
  });
255
255
  }
256
256
  }
257
+
258
+ /**
259
+ * Creates object-position style based on focal point coordinates
260
+ *
261
+ * @param {Object} focalPoint - Focal point coordinates { x, y }
262
+ * @returns {Object} Style object with objectPosition property
263
+ */
264
+ export const getFocalPointStyle = (focalPoint) => {
265
+ if (!focalPoint) {
266
+ return { objectPosition: '50% 50%' };
267
+ }
268
+
269
+ // Handle edge case where x or y is 0
270
+ const x = (focalPoint.x !== undefined && focalPoint.x !== null) ? focalPoint.x * 100 : 50;
271
+ const y = (focalPoint.y !== undefined && focalPoint.y !== null) ? focalPoint.y * 100 : 50;
272
+
273
+ return { objectPosition: `${x}% ${y}%` };
274
+ };
275
+