@pushwoosh/smart-components-kit 1.22.0 → 1.23.0

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.
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
2
+ import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
3
  import { Button, Chip, Drawer, DrawerBody, DrawerHeader, DrawerTitle, ModalConfirm, NativeInput, useToast } from '@pushwoosh/dumb-components';
4
+ import { Color } from '@pushwoosh/kit-constants';
4
5
  import { Columns, Vertical } from '@pushwoosh/kit-helpers';
5
6
  import { CloseIcon, TrashIcon } from '@pushwoosh/kit-icons';
6
7
  import { Text } from '@pushwoosh/kit-typography';
@@ -10,7 +11,7 @@ import { CENTER_FOCAL, getCropWindow, getRequestedSize, getStoredOrigin, getStor
10
11
  import { useDeleteMediaFile } from './hooks';
11
12
  import { getAspectRatio, getCropCaption, getThumbUrl } from './mediaFile.utils';
12
13
  import { useLang } from './polyglot';
13
- import { Chips, CropStrip, CropThumb, CropThumbButton, CropThumbCaption, CropThumbDelete, CropThumbImage, FieldLabel, PreviewFrame, PreviewImage, PreviewStage, Segment, SegmentItem, SizeInput } from './styles';
14
+ import { Chips, CropStrip, CropThumb, CropThumbButton, CropThumbCaption, CropThumbDelete, CropThumbImage, FieldLabel, PreviewFrame, PreviewImage, PreviewStage, SizeInput } from './styles';
14
15
  const RATIOS = [{
15
16
  value: 'ASPECT_RATIO_1_1',
16
17
  label: '1:1',
@@ -58,6 +59,15 @@ const RATIOS = [{
58
59
  }];
59
60
  const DEFAULT_SIZE = '1080';
60
61
  const DEFAULT_QUALITY = '90';
62
+ const flashRecalculated = input => {
63
+ if (!input || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
64
+ input.animate([{
65
+ color: Color.PRIMARY
66
+ }, {}], {
67
+ duration: 600,
68
+ easing: 'ease-out'
69
+ });
70
+ };
61
71
  export const CreateCropDrawer = ({
62
72
  application,
63
73
  original,
@@ -76,7 +86,7 @@ export const CreateCropDrawer = ({
76
86
  } = useToast();
77
87
  const deleteMediaFile = useDeleteMediaFile(application);
78
88
  const [ratio, setRatio] = useState('original');
79
- const [dimension, setDimension] = useState('width');
89
+ const [lastEdited, setLastEdited] = useState('width');
80
90
  const [size, setSize] = useState(DEFAULT_SIZE);
81
91
  const [quality, setQuality] = useState(DEFAULT_QUALITY);
82
92
  const [isCreating, setIsCreating] = useState(false);
@@ -89,7 +99,7 @@ export const CreateCropDrawer = ({
89
99
  useEffect(() => {
90
100
  if (original) {
91
101
  setRatio('original');
92
- setDimension('width');
102
+ setLastEdited('width');
93
103
  setSize(DEFAULT_SIZE);
94
104
  setQuality(DEFAULT_QUALITY);
95
105
  setViewedCrop(null);
@@ -119,7 +129,7 @@ export const CreateCropDrawer = ({
119
129
  const {
120
130
  width: outputWidth,
121
131
  height: outputHeight
122
- } = getRequestedSize(ratioW, ratioH, dimension, sizeValue);
132
+ } = getRequestedSize(ratioW, ratioH, lastEdited, sizeValue);
123
133
  const srcW = originalMeta?.width ?? 0;
124
134
  const srcH = originalMeta?.height ?? 0;
125
135
  const canPlaceWindow = !isOriginalView && srcW > 0 && srcH > 0;
@@ -134,20 +144,34 @@ export const CreateCropDrawer = ({
134
144
  height: outputHeight
135
145
  };
136
146
  const isCapped = storedSize.width !== outputWidth || storedSize.height !== outputHeight;
137
- // Both inputs are always visible: the driving one is editable, the other is
138
- // read-only and shows the value computed from the aspect ratio.
147
+ // Both inputs are editable; the last edited one drives the size, the other follows the ratio.
139
148
  const computedText = value => isSizeValid ? String(value) : '';
140
- const widthText = dimension === 'width' ? size : computedText(outputWidth);
141
- const heightText = dimension === 'height' ? size : computedText(outputHeight);
142
- const handleDimensionChange = next => {
143
- if (next === dimension) return;
144
- // Seed the newly editable field with its computed value so the visible
145
- // numbers stay put and only editability swaps.
146
- if (isSizeValid) {
147
- setSize(String(next === 'width' ? outputWidth : outputHeight));
148
- }
149
- setDimension(next);
149
+ const widthText = lastEdited === 'width' ? size : computedText(outputWidth);
150
+ const heightText = lastEdited === 'height' ? size : computedText(outputHeight);
151
+ const handleSizeChange = (edited, value) => {
152
+ setLastEdited(edited);
153
+ setSize(value);
150
154
  };
155
+ const widthInputRef = useRef(null);
156
+ const heightInputRef = useRef(null);
157
+ const prevSizeRef = useRef({
158
+ original,
159
+ ratio,
160
+ width: widthText,
161
+ height: heightText
162
+ });
163
+ useLayoutEffect(() => {
164
+ const prev = prevSizeRef.current;
165
+ prevSizeRef.current = {
166
+ original,
167
+ ratio,
168
+ width: widthText,
169
+ height: heightText
170
+ };
171
+ if (isOriginalView || prev.original !== original || prev.ratio === 'original') return;
172
+ if (lastEdited === 'width' && heightText && heightText !== prev.height) flashRecalculated(heightInputRef.current);
173
+ if (lastEdited === 'height' && widthText && widthText !== prev.width) flashRecalculated(widthInputRef.current);
174
+ }, [original, ratio, isOriginalView, lastEdited, widthText, heightText]);
151
175
  // A crop is the same one only at the same ratio, output size and window position.
152
176
  const duplicateExists = !isOriginalView && isSizeValid && existingCrops.some(crop => {
153
177
  if (crop.meta?.type !== 'imageMeta') return false;
@@ -172,7 +196,7 @@ export const CreateCropDrawer = ({
172
196
  uuid: parent.uuid,
173
197
  ratio,
174
198
  size: {
175
- type: dimension,
199
+ type: lastEdited,
176
200
  data: sizeValue
177
201
  },
178
202
  ...(isJpeg ? {
@@ -265,45 +289,31 @@ export const CreateCropDrawer = ({
265
289
  message: 'SMART.MEDIA_STORAGE.CREATE_CROP.SIZE_LABEL'
266
290
  })
267
291
  }), _jsxs(Columns, {
268
- "$sizes": "auto 1fr 1fr",
292
+ "$sizes": "1fr auto 1fr",
269
293
  "$gap": 8,
270
294
  "$alignItems": "center",
271
- children: [_jsxs(Segment, {
272
- children: [_jsx(SegmentItem, {
273
- type: "button",
274
- "$active": dimension === 'width',
275
- onClick: () => handleDimensionChange('width'),
276
- children: lang({
277
- message: 'SMART.MEDIA_STORAGE.CREATE_CROP.WIDTH'
278
- })
279
- }), _jsx(SegmentItem, {
280
- type: "button",
281
- "$active": dimension === 'height',
282
- onClick: () => handleDimensionChange('height'),
283
- children: lang({
284
- message: 'SMART.MEDIA_STORAGE.CREATE_CROP.HEIGHT'
285
- })
286
- })]
287
- }), _jsx(SizeInput, {
295
+ children: [_jsx(SizeInput, {
288
296
  type: "number",
289
297
  min: 1,
290
298
  value: widthText,
291
- readOnly: dimension !== 'width',
292
- "$isErrored": dimension === 'width' && !isSizeValid,
299
+ "$isErrored": lastEdited === 'width' && !isSizeValid,
300
+ ref: widthInputRef,
293
301
  "aria-label": lang({
294
302
  message: 'SMART.MEDIA_STORAGE.CREATE_CROP.WIDTH'
295
303
  }),
296
- onChange: event => setSize(event.target.value)
304
+ onChange: event => handleSizeChange('width', event.target.value)
305
+ }), _jsx(Text, {
306
+ children: "\u00D7"
297
307
  }), _jsx(SizeInput, {
298
308
  type: "number",
299
309
  min: 1,
300
310
  value: heightText,
301
- readOnly: dimension !== 'height',
302
- "$isErrored": dimension === 'height' && !isSizeValid,
311
+ "$isErrored": lastEdited === 'height' && !isSizeValid,
312
+ ref: heightInputRef,
303
313
  "aria-label": lang({
304
314
  message: 'SMART.MEDIA_STORAGE.CREATE_CROP.HEIGHT'
305
315
  }),
306
- onChange: event => setSize(event.target.value)
316
+ onChange: event => handleSizeChange('height', event.target.value)
307
317
  })]
308
318
  })]
309
319
  }), _jsxs(Vertical, {
@@ -115,7 +115,7 @@ export const FieldLabel = styled.div.withConfig({
115
115
  export const SizeInput = styled(NativeInput).withConfig({
116
116
  displayName: "SizeInput",
117
117
  componentId: "sc-uu3cbo-25"
118
- })(["&[readonly]{background:", ";color:", ";}"], Color.SOFT_LIGHT, Color.LOCKED);
118
+ })(["-webkit-text-fill-color:currentColor;"]);
119
119
  export const Chips = styled.div.withConfig({
120
120
  displayName: "Chips",
121
121
  componentId: "sc-uu3cbo-26"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/smart-components-kit",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "Stateful, data-connected React components shared by Pushwoosh micro-frontends",
5
5
  "repository": {
6
6
  "type": "git",