@cyberyozh/text-editor 0.3.1 → 0.3.2

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.
@@ -563,6 +563,35 @@
563
563
  background: var(--ui-secondary-bg);
564
564
  color: var(--ui-text-primary);
565
565
  }
566
+ .image-dialog-error {
567
+ position: relative;
568
+ margin-bottom: 16px;
569
+ padding: 8px 12px;
570
+ border: 1px solid var(--ui-error-border);
571
+ border-radius: 4px;
572
+ background-color: var(--ui-error-bg);
573
+ color: var(--ui-error-text);
574
+ font-size: 13px;
575
+ }
576
+ .image-dialog-error-clear-button {
577
+ display: flex;
578
+ position: absolute;
579
+ top: 8px;
580
+ right: 8px;
581
+ align-items: center;
582
+ justify-content: center;
583
+ width: 20px;
584
+ height: 20px;
585
+ padding: 0;
586
+ border: none;
587
+ background-color: transparent;
588
+ cursor: pointer;
589
+ }
590
+ .image-dialog-error-clear-button svg {
591
+ width: 14px;
592
+ height: 14px;
593
+ color: var(--ui-error-text);
594
+ }
566
595
 
567
596
  /* src/styles/components.css */
568
597
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyberyozh/text-editor",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Rich text editor library based on Tiptap for CyberYozh",
5
5
  "author": "CyberYozh",
6
6
  "license": "MIT",
@@ -100,3 +100,35 @@
100
100
  background: var(--ui-secondary-bg);
101
101
  color: var(--ui-text-primary);
102
102
  }
103
+
104
+ .image-dialog-error {
105
+ position: relative;
106
+ margin-bottom: 16px;
107
+ padding: 8px 12px;
108
+ border: 1px solid var(--ui-error-border);
109
+ border-radius: 4px;
110
+ background-color: var(--ui-error-bg);
111
+ color: var(--ui-error-text);
112
+ font-size: 13px;
113
+ }
114
+
115
+ .image-dialog-error-clear-button {
116
+ display: flex;
117
+ position: absolute;
118
+ top: 8px;
119
+ right: 8px;
120
+ align-items: center;
121
+ justify-content: center;
122
+ width: 20px;
123
+ height: 20px;
124
+ padding: 0;
125
+ border: none;
126
+ background-color: transparent;
127
+ cursor: pointer;
128
+ }
129
+
130
+ .image-dialog-error-clear-button svg {
131
+ width: 14px;
132
+ height: 14px;
133
+ color: var(--ui-error-text);
134
+ }
@@ -90,13 +90,26 @@ export const ImageDialog = ({
90
90
  height: constrainedDimensions.height,
91
91
  });
92
92
 
93
+ const [fileUploadError, setFileUploadError] = useState<Error | null>(null);
94
+
93
95
  const handleSelectFile = (file: File | undefined) => {
96
+ setFileUploadError(null);
97
+
94
98
  if (!file) return;
95
99
 
100
+ if (!file.type.startsWith('image/')) {
101
+ setFileUploadError(new Error(translations.imageDialog.invalidFileType));
102
+ return;
103
+ }
104
+
96
105
  const imageUrl = URL.createObjectURL(file);
97
106
  const img = new Image();
98
107
  img.src = imageUrl;
99
108
 
109
+ img.onerror = () => {
110
+ setFileUploadError(new Error(translations.imageDialog.invalidFileType));
111
+ };
112
+
100
113
  img.onload = () => {
101
114
  const naturalWidth = img.width;
102
115
  const naturalHeight = img.height;
@@ -189,6 +202,7 @@ export const ImageDialog = ({
189
202
  if (!formState.file) return;
190
203
 
191
204
  setIsUploading(true);
205
+
192
206
  try {
193
207
  const result = await uploadHandler(formState.file);
194
208
 
@@ -204,6 +218,8 @@ export const ImageDialog = ({
204
218
  });
205
219
 
206
220
  handleClose();
221
+ } catch (error) {
222
+ setFileUploadError(error instanceof Error ? error : new Error(translations.imageDialog.unknownError));
207
223
  } finally {
208
224
  setIsUploading(false);
209
225
  }
@@ -223,7 +239,9 @@ export const ImageDialog = ({
223
239
  handleClose();
224
240
  };
225
241
 
226
- const handleSave = mode === "edit" ? handleEditImage : handleAddImage;
242
+ const isEditMode = mode === "edit";
243
+ const canSave = isEditMode || Boolean(formState.file);
244
+ const handleSave = isEditMode ? handleEditImage : handleAddImage;
227
245
 
228
246
  const handleClose = () => {
229
247
  clearImageFormFields();
@@ -315,6 +333,19 @@ export const ImageDialog = ({
315
333
  )}
316
334
  </div>
317
335
  </div>
336
+ {fileUploadError && (
337
+ <div className="image-dialog-error">
338
+ {fileUploadError.message || translations.imageDialog.unknownError}
339
+ <button
340
+ type="button"
341
+ className="image-dialog-error-clear-button"
342
+ onClick={() => setFileUploadError(null)}
343
+ aria-label={translations.imageDialog.clearError}
344
+ >
345
+ <TiptapX />
346
+ </button>
347
+ </div>
348
+ )}
318
349
  <div className="image-dialog-actions">
319
350
  <Button
320
351
  type="button"
@@ -328,6 +359,7 @@ export const ImageDialog = ({
328
359
  isLoading={isUploading}
329
360
  type="button"
330
361
  onClick={handleSave}
362
+ disabled={!canSave}
331
363
  >
332
364
  {saveButtonText}
333
365
  </Button>
@@ -17,11 +17,10 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
17
17
  }, [insertedImage, activeDialog]);
18
18
 
19
19
  function genericInsertionHandler(file: File | undefined, event: Event) {
20
- if (!file || !file.type.startsWith("image/")) return;
21
-
22
20
  event.preventDefault();
23
21
  event.stopPropagation();
24
22
 
23
+ if (!file || !file.type.startsWith("image/")) return;
25
24
  if (!imageHandlers?.upload) return;
26
25
 
27
26
  setInsertedImage(file);
@@ -112,7 +112,10 @@
112
112
  "captionLabel": "Image caption",
113
113
  "captionPlaceholder": "Description under image...",
114
114
  "save": "Save",
115
- "add": "Add"
115
+ "add": "Add",
116
+ "invalidFileType": "File is not an image",
117
+ "unknownError": "Unknown error",
118
+ "clearError": "Clear error"
116
119
  },
117
120
  "videoUpload": {
118
121
  "title": "Add video",
@@ -112,7 +112,10 @@
112
112
  "captionLabel": "Подпись к изображению",
113
113
  "captionPlaceholder": "Описание под изображением...",
114
114
  "save": "Сохранить",
115
- "add": "Добавить"
115
+ "add": "Добавить",
116
+ "invalidFileType": "Файл не является изображением",
117
+ "unknownError": "Неизвестная ошибка",
118
+ "clearError": "Сбросить ошибку"
116
119
  },
117
120
  "videoUpload": {
118
121
  "title": "Добавить видео",