@lumir-company/editor 0.4.9 → 0.4.10

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/README.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - [파일명 커스터마이징](#파일명-커스터마이징)
19
19
  - [커스텀 업로더](#2-커스텀-업로더)
20
20
  - [동영상 업로드 및 임베딩](#동영상-업로드-및-임베딩)
21
+ - [업로드 진행률 표시](#업로드-진행률-표시-이미지동영상-공통)
21
22
  - [이미지·동영상 업로드 상세 가이드](#이미지동영상-업로드-상세-가이드)
22
23
  - [이미지·비디오 삭제](#이미지비디오-삭제)
23
24
  - [HTML 미리보기](#html-미리보기)
@@ -457,6 +458,29 @@ const imageUrl = await s3Uploader(imageFile);
457
458
  - **지원 형식**: MP4, WebM, OGG
458
459
  - **삽입 경로**: 붙여넣기, 드래그 앤 드롭, 슬래시 메뉴("Video"), FloatingMenu 이미지/동영상 버튼
459
460
 
461
+ ### 업로드 진행률 표시 (이미지·동영상 공통)
462
+
463
+ S3 업로드 시 `s3Upload.onProgress` 콜백을 지정하면 업로드 진행률(0~100%)을 받을 수 있습니다. 에디터는 내부적으로 이 값을 사용해 업로드 중 툴바에 **"n%"** 를 표시합니다. 동영상처럼 대용량 파일은 브라우저가 `progress` 이벤트를 자주 보내지 않을 수 있어, 내부적으로 **보간 로직**을 적용해 0→100만 보이지 않고 중간 진행률이 부드럽게 갱신되도록 했습니다.
464
+
465
+ ```tsx
466
+ <LumirEditor
467
+ allowVideoUpload={true}
468
+ s3Upload={{
469
+ apiEndpoint: "/api/s3/presigned",
470
+ env: "production",
471
+ path: "videos",
472
+ appendUUID: true,
473
+ onProgress: (percent) => {
474
+ console.log(`업로드 진행률: ${percent}%`);
475
+ // 에디터 기본 UI에 이미 표시되며, 필요 시 자체 프로그레스 바 등에 연동 가능
476
+ },
477
+ }}
478
+ />
479
+ ```
480
+
481
+ - **동작**: S3 PUT 요청 시에만 호출됩니다. Presigned URL 요청 단계에서는 호출되지 않습니다.
482
+ - **표시**: `onProgress`를 넘기면 에디터 툴바에 `n%`가 자동 표시되며, 업로드 완료 시 숨겨집니다.
483
+
460
484
  ### 데이터 내부 동영상 임베딩
461
485
 
462
486
  동영상 블록은 `initialContent` / `onContentChange`에 포함됩니다. 저장 시 `{ type: "video", props: { url: "..." } }` 형태로 블록이 유지됩니다.
@@ -1087,6 +1111,11 @@ interface S3UploaderConfig {
1087
1111
  fileNameTransform?: (nameWithoutExt: string, file: File) => string; // 확장자 제외한 이름 변환
1088
1112
  appendUUID?: boolean; // true: 파일명 뒤에 UUID 추가 (확장자 앞에 삽입)
1089
1113
  preserveExtension?: boolean; // false: 확장자를 붙이지 않음 (기본: true)
1114
+
1115
+ // 선택 (업로드 동작)
1116
+ onProgress?: (percent: number) => void; // 업로드 진행률 0~100 콜백 (S3 PUT 시만 호출, 중간 진행률 보간 지원)
1117
+ uploadTimeoutMs?: number; // PUT 타임아웃(ms). 미설정 시 120000(120초). 대용량 동영상 시 연장 권장
1118
+ maxRetries?: number; // PUT 실패 시 재시도 횟수. 기본 2(최대 3회 시도)
1090
1119
  }
1091
1120
  ```
1092
1121
 
@@ -1109,6 +1138,9 @@ interface LumirEditorProps {
1109
1138
  fileNameTransform?: (nameWithoutExt: string, file: File) => string; // 확장자 제외한 이름 변환
1110
1139
  appendUUID?: boolean; // UUID 자동 추가 (확장자 앞)
1111
1140
  preserveExtension?: boolean; // 확장자 자동 붙이기 (기본: true)
1141
+ onProgress?: (percent: number) => void; // 업로드 진행률 0~100 (이미지·동영상 공통, 중간 진행률 보간)
1142
+ uploadTimeoutMs?: number; // PUT 타임아웃(ms). 기본 120000
1143
+ maxRetries?: number; // PUT 재시도 횟수. 기본 2
1112
1144
  };
1113
1145
 
1114
1146
  // === 콜백 ===
@@ -1352,6 +1384,15 @@ const url = await uploader(imageFile);
1352
1384
 
1353
1385
  ## 변경 로그
1354
1386
 
1387
+ ### v0.4.10
1388
+
1389
+ - **동영상·이미지 업로드 진행률 표시**
1390
+ - S3 업로드 시 진행률이 0만 보이다가 100으로 바로 완료되던 문제 개선
1391
+ - `xhr.upload.onprogress`와 **보간 타이머**를 함께 사용해 중간 진행률(0→…→100)이 부드럽게 갱신되도록 변경
1392
+ - 업로드 시작 직후 `onProgress(0)` 호출, 완료 시 `onProgress(100)` 보장
1393
+ - README: `s3Upload.onProgress` 설명 및 업로드 진행률 표시 섹션 추가
1394
+ - README: `S3UploaderConfig`에 `onProgress`, `uploadTimeoutMs`, `maxRetries` 문서화
1395
+
1355
1396
  ### v0.4.9
1356
1397
 
1357
1398
  - **업로드 용량·타임아웃 사용자 설정**
package/dist/index.js CHANGED
@@ -188,19 +188,54 @@ var createS3Uploader = (config) => {
188
188
  await new Promise((resolve, reject) => {
189
189
  const xhr = new XMLHttpRequest();
190
190
  xhr.timeout = uploadTimeoutMs;
191
+ let lastReported = -1;
192
+ const REPORT_INTERVAL_MS = 100;
193
+ const simulatedCap = 90;
194
+ let simulatedPercent = 0;
195
+ const simId = setInterval(() => {
196
+ if (simulatedPercent >= simulatedCap) return;
197
+ simulatedPercent = Math.min(simulatedCap, simulatedPercent + 3);
198
+ const p = Math.min(100, simulatedPercent);
199
+ if (p > lastReported) {
200
+ lastReported = p;
201
+ onProgress(p);
202
+ }
203
+ }, REPORT_INTERVAL_MS);
204
+ const clearSim = () => {
205
+ clearInterval(simId);
206
+ };
191
207
  xhr.upload.onprogress = (e) => {
192
208
  if (e.lengthComputable) {
193
- onProgress(Math.min(100, Math.round(e.loaded / e.total * 100)));
209
+ const p = Math.min(100, Math.round(e.loaded / e.total * 100));
210
+ if (p >= simulatedCap) clearSim();
211
+ const toReport = Math.max(p, simulatedPercent);
212
+ if (toReport > lastReported) {
213
+ lastReported = toReport;
214
+ onProgress(toReport);
215
+ }
194
216
  }
195
217
  };
196
218
  xhr.onload = () => {
197
- if (xhr.status >= 200 && xhr.status < 300) resolve();
198
- else reject(new Error(`Failed to upload file: ${xhr.statusText}`));
219
+ clearSim();
220
+ if (xhr.status >= 200 && xhr.status < 300) {
221
+ if (lastReported < 100) onProgress(100);
222
+ resolve();
223
+ } else {
224
+ reject(new Error(`Failed to upload file: ${xhr.statusText}`));
225
+ }
226
+ };
227
+ xhr.onerror = () => {
228
+ clearSim();
229
+ reject(new Error("Upload failed"));
230
+ };
231
+ xhr.ontimeout = () => {
232
+ clearSim();
233
+ reject(new Error("Upload timeout"));
199
234
  };
200
- xhr.onerror = () => reject(new Error("Upload failed"));
201
- xhr.ontimeout = () => reject(new Error("Upload timeout"));
202
235
  xhr.open("PUT", validatedPresignedUrl);
203
236
  xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
237
+ onProgress(0);
238
+ lastReported = 0;
204
239
  xhr.send(file);
205
240
  });
206
241
  } else {