sales-frontend-components 2.0.3 → 2.0.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/dist/index.cjs.js CHANGED
@@ -320,41 +320,46 @@ async function imageUrlToFileFetch(imageUrl) {
320
320
  const file = new File([blob], name, { type });
321
321
  return file;
322
322
  }
323
- async function imageUrlToFile(imageUrl) {
324
- const fileConvertTypeCookieKey = "dsp-debug-mode-file-convert-type";
325
- const fileConvertType = getCookie$1(fileConvertTypeCookieKey);
326
- if (fileConvertType === "fetch") {
323
+ async function imageUrlToFile(imageUrl, convertType) {
324
+ if (convertType === "fetch") {
327
325
  return await imageUrlToFileFetch(imageUrl);
328
326
  }
329
- if (fileConvertType === "xhr") {
327
+ if (convertType === "xhr") {
330
328
  return await imageUrlToFileWithXMLHttpRequest(imageUrl);
331
329
  }
332
330
  return await imageUrlToFileWithCanvas(imageUrl);
333
331
  }
334
332
  async function imageUrlToFileWithCanvas(imageUrl) {
335
333
  const newImage = new Image();
336
- newImage.src = imageUrl;
337
- newImage.crossOrigin = "Anonymous";
338
334
  return new Promise((resolve, reject) => {
339
335
  newImage.onload = () => {
340
336
  const canvas = document.createElement("canvas");
341
337
  canvas.width = newImage.width;
342
338
  canvas.height = newImage.height;
343
339
  const ctx = canvas.getContext("2d");
344
- if (ctx) {
340
+ if (!ctx) {
341
+ reject(new Error("Failed to get canvas 2d context."));
342
+ return;
343
+ }
344
+ try {
345
345
  ctx.drawImage(newImage, 0, 0);
346
- canvas.toBlob((blob) => {
347
- if (blob) {
348
- const ext = getExt(imageUrl);
349
- const file = new File([blob], `image.${ext}`, { type: blob.type });
350
- resolve(file);
351
- }
352
- });
346
+ const dataUrl = canvas.toDataURL();
347
+ const blob = base64ToBlob(dataUrl);
348
+ const fileExt = getExt(blob) ?? "png";
349
+ resolve(blobToFile(blob, `image.${fileExt}`));
350
+ } catch (error) {
351
+ reject(
352
+ new Error(
353
+ `imageUrlToFileWithCanvas failed for "${imageUrl}". ${error instanceof Error ? error.message : "Unknown error"}`
354
+ )
355
+ );
353
356
  }
354
357
  };
355
358
  newImage.onerror = (e) => {
356
359
  reject(JSON.stringify(e));
357
360
  };
361
+ newImage.crossOrigin = "Anonymous";
362
+ newImage.src = imageUrl;
358
363
  });
359
364
  }
360
365
  async function imageUrlToFileWithXMLHttpRequest(imageUrl) {
@@ -4548,7 +4553,7 @@ function resize(image, options = { ext: "jpeg", filesize: maxImageSize }) {
4548
4553
  });
4549
4554
  }
4550
4555
 
4551
- const genImageId = () => `camera-${Date.now()}-${Math.random()}`;
4556
+ const genImageId$1 = () => `camera-${Date.now()}-${Math.random()}`;
4552
4557
  function useCamera({
4553
4558
  onChange,
4554
4559
  resize: resizeOption = {
@@ -4566,7 +4571,8 @@ function useCamera({
4566
4571
  buttonText,
4567
4572
  initData,
4568
4573
  useNativeCamera = false,
4569
- responseFileType
4574
+ responseFileType,
4575
+ convertType = "canvas"
4570
4576
  } = {}) {
4571
4577
  const convertedInitData = initData?.map((data, index) => ({ ...data, id: String(index + 1) }));
4572
4578
  const [attachedPhotos, setAttachedPhotos] = React9.useState(convertedInitData || []);
@@ -4575,7 +4581,7 @@ function useCamera({
4575
4581
  };
4576
4582
  const imageHandler = async (file) => {
4577
4583
  const newPhoto = {
4578
- id: genImageId(),
4584
+ id: genImageId$1(),
4579
4585
  src: URL.createObjectURL(resizeOption ? await resize(file, resizeOption) : file),
4580
4586
  name: `\uC11C\uB958\uC0AC\uC9C4_${attachedPhotos.length + 1}`
4581
4587
  };
@@ -4590,7 +4596,7 @@ function useCamera({
4590
4596
  };
4591
4597
  const processImage = async (uri) => {
4592
4598
  if (responseFileType === "scheme") {
4593
- const file = await imageUrlToFile(uri);
4599
+ const file = await imageUrlToFile(uri, convertType);
4594
4600
  await imageHandler(file);
4595
4601
  } else {
4596
4602
  const file = await base64ToFile(uri, "image.jpg");
@@ -4635,10 +4641,171 @@ function useCamera({
4635
4641
  const imageIndex = attachedPhotos.findIndex((image) => image.id === imageId);
4636
4642
  if (imageIndex > -1) {
4637
4643
  const item = attachedPhotos.splice(imageIndex, 1);
4638
- item[0] && URL.revokeObjectURL(item[0].src);
4644
+ if (item[0]) {
4645
+ URL.revokeObjectURL(item[0].src);
4646
+ }
4639
4647
  setAttachedPhotos([...attachedPhotos]);
4640
- onDelete && onDelete(imageId);
4648
+ if (onDelete) {
4649
+ onDelete(imageId);
4650
+ }
4651
+ }
4652
+ };
4653
+ const deleteAllImages = () => {
4654
+ attachedPhotos.forEach((image) => {
4655
+ URL.revokeObjectURL(image.src);
4656
+ if (onDelete) {
4657
+ onDelete(image.id);
4658
+ }
4659
+ });
4660
+ setAttachedPhotos([]);
4661
+ };
4662
+ const CameraComponent = () => /* @__PURE__ */ jsxRuntime.jsx(
4663
+ Attachment,
4664
+ {
4665
+ show: !!show,
4666
+ onAddPhoto: onClick,
4667
+ onRemovePhoto: deleteImage,
4668
+ photos: attachedPhotos,
4669
+ type,
4670
+ buttonText
4641
4671
  }
4672
+ );
4673
+ React9.useEffect(() => {
4674
+ return () => {
4675
+ attachedPhotos.forEach((image) => {
4676
+ URL.revokeObjectURL(image.src);
4677
+ });
4678
+ };
4679
+ }, []);
4680
+ return {
4681
+ onClick,
4682
+ getImage: findImage,
4683
+ deleteImage,
4684
+ deleteAllImages,
4685
+ attachedPhotos,
4686
+ Attachment: CameraComponent,
4687
+ addImage: (data) => {
4688
+ setAttachedPhotos([
4689
+ ...attachedPhotos,
4690
+ ...data.map((item) => {
4691
+ let blobUrl = "";
4692
+ if (item.data instanceof Blob) {
4693
+ blobUrl = URL.createObjectURL(item.data);
4694
+ } else if (typeof item.data === "string") {
4695
+ blobUrl = URL.createObjectURL(base64ToBlob(item.data));
4696
+ }
4697
+ const newPhoto = {
4698
+ id: genImageId$1(),
4699
+ src: blobUrl,
4700
+ name: item.name || `\uC11C\uB958\uC0AC\uC9C4_${attachedPhotos.length + 1}`
4701
+ };
4702
+ return newPhoto;
4703
+ })
4704
+ ]);
4705
+ }
4706
+ };
4707
+ }
4708
+
4709
+ const genImageId = () => `camera-${Date.now()}-${Math.random()}`;
4710
+ function useCameraV2({
4711
+ onChange,
4712
+ resize: resizeOption = {
4713
+ processType: "mixed",
4714
+ resizeRatio: 5,
4715
+ width: 1920,
4716
+ // 300kb
4717
+ filesize: 300 * 1024,
4718
+ ext: "jpeg"
4719
+ },
4720
+ cameraOnly,
4721
+ onDelete,
4722
+ show,
4723
+ type = "multiple",
4724
+ buttonText,
4725
+ initData,
4726
+ useNativeCamera = false,
4727
+ responseFileType,
4728
+ convertType = "canvas"
4729
+ }) {
4730
+ const convertedInitData = initData?.map((data, index) => ({ ...data, id: String(index + 1) }));
4731
+ const [attachedPhotos, setAttachedPhotos] = React9.useState(convertedInitData || []);
4732
+ const findImage = async (imageId) => {
4733
+ const photo = attachedPhotos.find((image) => image.id === imageId);
4734
+ if (!photo) {
4735
+ throw new Error("[use-camera-v2] : Image not found");
4736
+ }
4737
+ const file = await imageUrlToFile(photo?.src, convertType);
4738
+ const convertedPhoto = {
4739
+ ...photo,
4740
+ src: URL.createObjectURL(resizeOption ? await resize(file, resizeOption) : file)
4741
+ };
4742
+ return convertedPhoto;
4743
+ };
4744
+ const imageHandler = async (uri) => {
4745
+ const newPhoto = {
4746
+ id: genImageId(),
4747
+ src: uri,
4748
+ name: `\uC11C\uB958\uC0AC\uC9C4_${attachedPhotos.length + 1}`
4749
+ };
4750
+ if (type === "single") {
4751
+ setAttachedPhotos([newPhoto]);
4752
+ } else {
4753
+ setAttachedPhotos([...attachedPhotos, newPhoto]);
4754
+ }
4755
+ if (onChange) {
4756
+ const file = await imageUrlToFile(uri, convertType);
4757
+ onChange(file);
4758
+ }
4759
+ };
4760
+ const onClick = () => {
4761
+ if (useNativeCamera) {
4762
+ salesFrontendBridge.Bridge.native.documentCapture({ responseFileType }).then(async ({ uri }) => {
4763
+ imageHandler(uri);
4764
+ });
4765
+ } else {
4766
+ const input = document.createElement("input");
4767
+ input.type = "file";
4768
+ input.accept = "image/*";
4769
+ if (cameraOnly) {
4770
+ input.capture = "camera";
4771
+ }
4772
+ input.addEventListener("change", async (event) => {
4773
+ const target = event.target;
4774
+ const { files } = target;
4775
+ if (files && files.length > 0) {
4776
+ const file = files[0];
4777
+ if (file) {
4778
+ await imageHandler(URL.createObjectURL(file));
4779
+ }
4780
+ }
4781
+ document.body.removeChild(input);
4782
+ });
4783
+ input.style.display = "none";
4784
+ document.body.appendChild(input);
4785
+ input.click();
4786
+ }
4787
+ };
4788
+ const deleteImage = (imageId) => {
4789
+ const imageIndex = attachedPhotos.findIndex((image) => image.id === imageId);
4790
+ if (imageIndex > -1) {
4791
+ const item = attachedPhotos.splice(imageIndex, 1);
4792
+ if (item[0]) {
4793
+ URL.revokeObjectURL(item[0].src);
4794
+ }
4795
+ setAttachedPhotos([...attachedPhotos]);
4796
+ if (onDelete) {
4797
+ onDelete(imageId);
4798
+ }
4799
+ }
4800
+ };
4801
+ const deleteAllImages = () => {
4802
+ attachedPhotos.forEach((image) => {
4803
+ URL.revokeObjectURL(image.src);
4804
+ if (onDelete) {
4805
+ onDelete(image.id);
4806
+ }
4807
+ });
4808
+ setAttachedPhotos([]);
4642
4809
  };
4643
4810
  const CameraComponent = () => /* @__PURE__ */ jsxRuntime.jsx(
4644
4811
  Attachment,
@@ -4662,6 +4829,7 @@ function useCamera({
4662
4829
  onClick,
4663
4830
  getImage: findImage,
4664
4831
  deleteImage,
4832
+ deleteAllImages,
4665
4833
  attachedPhotos,
4666
4834
  Attachment: CameraComponent,
4667
4835
  addImage: (data) => {
@@ -7122,6 +7290,7 @@ exports.testSignatureBase64Data = testSignatureBase64Data;
7122
7290
  exports.useAddressComponent = useAddressComponent;
7123
7291
  exports.useBankStockSearch = useBankStockSearch;
7124
7292
  exports.useCamera = useCamera;
7293
+ exports.useCameraV2 = useCameraV2;
7125
7294
  exports.useCanvasPaint = useCanvasPaint;
7126
7295
  exports.useCustomerSearch = useCustomerSearch;
7127
7296
  exports.useDownloader = useDownloader;
package/dist/index.d.ts CHANGED
@@ -103,82 +103,93 @@ interface StepIndicatorProps {
103
103
 
104
104
  declare const StepIndicator: ({ items, onClickItem, currentIndex, defaultValue, dotCount, isLoading }: StepIndicatorProps) => react_jsx_runtime.JSX.Element;
105
105
 
106
+ /**
107
+ * 카메라 훅이 화면에 표시하는 첨부 이미지 메타데이터입니다.
108
+ * 실제 업로드용 원본 `File` 자체가 아니라, 목록 렌더링과 삭제 처리를 위한 식별자/미리보기 정보를 담습니다.
109
+ */
106
110
  interface AttachedPhoto {
111
+ /** 첨부 이미지를 고유하게 식별하는 값입니다. 삭제, 교체, 정렬 시 기준 키로 사용합니다. */
107
112
  id: string;
113
+ /** 썸네일 또는 미리보기에 사용할 이미지 URL입니다. `blob:` URL, data URL, 원격 URL 등이 들어올 수 있습니다. */
108
114
  src: string;
115
+ /** 화면에 함께 표시할 파일명입니다. 없으면 UI에서 이름 표시를 생략할 수 있습니다. */
109
116
  name?: string;
110
117
  }
118
+ /**
119
+ * `useCamera` 훅 동작을 제어하는 옵션입니다.
120
+ * 호출부에서 첨부 UI 노출 방식, 카메라 실행 방식, 변환/리사이즈 정책을 함께 정의할 때 사용합니다.
121
+ */
111
122
  interface cameraOptions {
123
+ /**
124
+ * 화면 첫 렌더링 시 미리 보여줄 기존 첨부 이미지 목록입니다.
125
+ * `id`는 훅 내부에서 자동 생성하므로 호출부에서는 `src`, `name`만 전달하면 됩니다.
126
+ */
112
127
  initData?: Omit<AttachedPhoto, 'id'>[];
128
+ /** `Attachment` 컴포넌트를 즉시 노출할지 여부입니다. 별도 버튼으로 열고 싶다면 `false`를 사용합니다. */
113
129
  show: boolean;
130
+ /** 첨부 UI 표시 형태입니다. 단일 첨부, 다중 첨부, 선형 레이아웃 중 하나를 선택합니다. */
114
131
  type: cameraItemType;
115
- onChange: (file: File) => void;
116
- onDelete: (id: string) => void;
132
+ /** 사진이 추가된 직후 호출되는 콜백입니다. 리사이즈 전 원본 `File`을 전달받아 후속 업로드 처리에 사용합니다. */
133
+ onChange?: (file: File) => void;
134
+ /** 사용자가 기존 첨부 사진을 삭제했을 때 호출되는 콜백입니다. 전달값은 삭제된 사진의 `id`입니다. */
135
+ onDelete?: (id: string) => void;
136
+ /** 앱 환경에서 브리지 기반 네이티브 카메라/문서 캡처를 사용할지 여부입니다. */
117
137
  useNativeCamera?: boolean;
138
+ /** 웹 `input[type="file"]` 사용 시 앨범/파일 선택 없이 카메라 촬영만 허용할지 여부입니다. */
118
139
  cameraOnly: boolean;
140
+ /** 기본 첨부 버튼 문구를 덮어쓸 때 사용하는 텍스트입니다. */
119
141
  buttonText?: string;
120
- responseFileType?: 'base64' | 'scheme';
121
- resize: {
142
+ /**
143
+ * 네이티브 캡처 결과의 응답 형식입니다.
144
+ * `scheme`은 파일/커스텀 스킴 URI를, `base64`는 Base64 문자열을 반환하도록 요청합니다.
145
+ */
146
+ responseFileType: 'base64' | 'scheme';
147
+ /**
148
+ * 이미지 URL을 `File`로 바꿀 때 사용할 변환 방식입니다.
149
+ * `fetch`, `xhr`, `canvas` 중 환경 제약과 호환성에 맞는 전략을 선택할 수 있습니다.
150
+ */
151
+ convertType: 'fetch' | 'xhr' | 'canvas';
152
+ /** 첨부 후 이미지 크기와 용량을 줄이기 위한 리사이즈 옵션입니다. */
153
+ resize?: {
154
+ /** 반복 축소 시 한 번에 줄일 비율입니다. `5`면 매 반복마다 약 5%씩 축소합니다. */
122
155
  resizeRatio?: number;
156
+ /** 어떤 기준으로 축소할지 결정합니다. 품질만, 픽셀 크기만, 또는 둘 다 조합해 처리할 수 있습니다. */
123
157
  processType?: 'quality' | 'px' | 'mixed';
158
+ /** 최종 목표 너비(px)입니다. 높이만 없는 경우 원본 비율을 유지해 자동 계산합니다. */
124
159
  width?: number;
160
+ /** 최종 목표 높이(px)입니다. 너비만 없는 경우 원본 비율을 유지해 자동 계산합니다. */
125
161
  height?: number;
162
+ /** 최종 목표 파일 크기(byte)입니다. 예: `300 * 1024`는 약 300KB입니다. */
126
163
  filesize?: number;
164
+ /** 출력 이미지 확장자입니다. 일반적으로 `jpeg` 또는 `png`를 사용합니다. */
127
165
  ext?: string;
128
166
  };
129
167
  }
130
168
  type cameraItemType = 'single' | 'multiple' | 'linear';
131
-
132
169
  interface AddImageInfo {
133
170
  data: Blob | string;
134
171
  name?: string;
135
172
  }
136
- /**
137
- * @param options Partial<cameraOptions>
138
- * initData
139
- * 화면최초 로딩시 초기값
140
- *
141
- * show
142
- * boolean
143
- * 화면진입시, Attachment 컴포넌트 노출여부, 별도의 버튼을 통해 호출할 경우 false.
144
- *
145
- * type
146
- * 'single' | 'multiple' | 'linear' Attachment 컴포넌트 타입
147
- *
148
- * onChange
149
- * (file: File) => void;
150
- *
151
- * onDelete
152
- * (id: string) => void;
153
- *
154
- * useNativeCamera
155
- * boolean
156
- * 앱환경에서, 네이티브의 카메라를 호출할경우 사용
157
- *
158
- * cameraOnly
159
- * boolean
160
- * useNativeCamera가 false인 경우에만 사용
161
- *
162
- * buttonText
163
- * string
164
- * Attachment 컴포넌트에 버튼을 표시할 경우에만 사용
165
- *
166
- * resize
167
- * 이미지가 입력된후 리사이즈를 할경우에만 사용
168
- * resize: {
169
- * width?: number;
170
- * height?: number;
171
- * filesize?: number;
172
- * ext?: string;
173
- * };
174
- *
175
- * responseFileType fetch로 이미지자원을 가져올때 ios이슈가 있어서,
176
- * scheme, base64 두가지 타입으로 설정가능하도록 변경됨
177
- */
178
- declare function useCamera({ onChange, resize: resizeOption, cameraOnly, onDelete, show, type, buttonText, initData, useNativeCamera, responseFileType }?: Partial<cameraOptions>): {
173
+
174
+ declare function useCamera({ onChange, resize: resizeOption, cameraOnly, onDelete, show, type, buttonText, initData, useNativeCamera, responseFileType, convertType }?: Partial<cameraOptions>): {
179
175
  onClick: () => void;
180
176
  getImage: (imageId: string) => AttachedPhoto | undefined;
181
177
  deleteImage: (imageId: string) => void;
178
+ deleteAllImages: () => void;
179
+ attachedPhotos: AttachedPhoto[];
180
+ Attachment: () => react_jsx_runtime.JSX.Element;
181
+ addImage: (data: AddImageInfo[]) => void;
182
+ };
183
+
184
+ declare function useCameraV2({ onChange, resize: resizeOption, cameraOnly, onDelete, show, type, buttonText, initData, useNativeCamera, responseFileType, convertType }: cameraOptions): {
185
+ onClick: () => void;
186
+ getImage: (imageId: string) => Promise<{
187
+ src: string;
188
+ id: string;
189
+ name?: string;
190
+ }>;
191
+ deleteImage: (imageId: string) => void;
192
+ deleteAllImages: () => void;
182
193
  attachedPhotos: AttachedPhoto[];
183
194
  Attachment: () => react_jsx_runtime.JSX.Element;
184
195
  addImage: (data: AddImageInfo[]) => void;
@@ -1006,5 +1017,5 @@ interface UseTermsReturn<T> {
1006
1017
  */
1007
1018
  declare function useTerms<T extends object>(initialValue: T): UseTermsReturn<T>;
1008
1019
 
1009
- export { AUTH_TEMPLATE_CODES, Attachment, BANK_STOCK_ICON_LIST, BANK_STOCK_SEARCH_MODAL_TABS, BankStockSearchModal, CustomerSearch, CustomerSearchModal, DeaCustomerSearchModal, DudDownload, DudUpload, EmployeeSearchModal, GtmIframe, HookFormCheckbox, HookFormCheckboxButton, HookFormDatePickerRenew, HookFormDateRangePickerRenew, HookFormSearchJobField, HookFormSegmentGroup, HookFormSelect, HookFormTextField, JobVehicleSearchModal, OrganizationSearchModal, RATING_DATA, RIV_SEARCH_PARAM_MAP, RivModalIframe, StepIndicator, TermsCancerCollectQR, TermsCancerMarketing, TermsCancerProvideQR, TermsCancerSystem, TermsCheckboxButton, TermsDesign, TermsExecution, TermsLoan, TermsMarketing, TermsMarketingCollectQR, TermsMarketingProviderQR, TermsMobileCard, TermsRadio, TermsRatingBar, TermsSignature, TermsTransfer, VERIFICATION_CODES, highlightOnSearchKeyword, resize, testSignatureBase64Data, useAddressComponent, useBankStockSearch, useCamera, useCanvasPaint, useCustomerSearch, useDownloader, useJobSearchModal, useJobVehicleSearch, useJobVehicleSearchModal, useNationalityComponent, useNxlOneModal, useRemoteIdentityVerification, useRemoteIdentityVerificationIframe, useSearchAddress, useSearchNationality, useSearchVisa, useTerms, useVisaComponent };
1020
+ export { AUTH_TEMPLATE_CODES, Attachment, BANK_STOCK_ICON_LIST, BANK_STOCK_SEARCH_MODAL_TABS, BankStockSearchModal, CustomerSearch, CustomerSearchModal, DeaCustomerSearchModal, DudDownload, DudUpload, EmployeeSearchModal, GtmIframe, HookFormCheckbox, HookFormCheckboxButton, HookFormDatePickerRenew, HookFormDateRangePickerRenew, HookFormSearchJobField, HookFormSegmentGroup, HookFormSelect, HookFormTextField, JobVehicleSearchModal, OrganizationSearchModal, RATING_DATA, RIV_SEARCH_PARAM_MAP, RivModalIframe, StepIndicator, TermsCancerCollectQR, TermsCancerMarketing, TermsCancerProvideQR, TermsCancerSystem, TermsCheckboxButton, TermsDesign, TermsExecution, TermsLoan, TermsMarketing, TermsMarketingCollectQR, TermsMarketingProviderQR, TermsMobileCard, TermsRadio, TermsRatingBar, TermsSignature, TermsTransfer, VERIFICATION_CODES, highlightOnSearchKeyword, resize, testSignatureBase64Data, useAddressComponent, useBankStockSearch, useCamera, useCameraV2, useCanvasPaint, useCustomerSearch, useDownloader, useJobSearchModal, useJobVehicleSearch, useJobVehicleSearchModal, useNationalityComponent, useNxlOneModal, useRemoteIdentityVerification, useRemoteIdentityVerificationIframe, useSearchAddress, useSearchNationality, useSearchVisa, useTerms, useVisaComponent };
1010
1021
  export type { AddImageInfo, AttachedPhoto, AttachmentProps, AuthCodeSet, AuthStep, BankStockSearchModalProps, BaseTermsProps, CustomerSearchProps, DownloadProps, DownloadTargetInfo, DownloaderProps, FormFactor, HookFormCheckboxButtonProps, HookFormCheckboxProps, HookFormDatePickerRenewProps, HookFormDateRangePickerRenewProps, HookFormSearchJobFieldProps, HookFormSegmentGroupProps, HookFormSelectProps, HookFormTextFieldProps, InitSearchParams, PaintProps, Pen, RemoteIdentityVerificationSuccess, RivModalIframeProps, RivModalIframeReturnProps, RivUrlParams, SearchInputProps, StepIndicatorProps, StepItem, TermsCancerMarketingData, TermsCancerMarketingProps, TermsCancerSystemData, TermsCancerSystemDataProps, TermsDesignData, TermsDesignProps, TermsExecutionData, TermsExecutionProps, TermsLoanData, TermsLoanDataProps, TermsMarketingData, TermsMarketingProps, TermsRadioOption, TermsRatingType, TermsSignatureData, UseRemoteIdentityVerificationProps, UseTermsReturn, Vehicle, VerificationResponse, cameraItemType, cameraOptions };
package/dist/index.esm.js CHANGED
@@ -318,41 +318,46 @@ async function imageUrlToFileFetch(imageUrl) {
318
318
  const file = new File([blob], name, { type });
319
319
  return file;
320
320
  }
321
- async function imageUrlToFile(imageUrl) {
322
- const fileConvertTypeCookieKey = "dsp-debug-mode-file-convert-type";
323
- const fileConvertType = getCookie$1(fileConvertTypeCookieKey);
324
- if (fileConvertType === "fetch") {
321
+ async function imageUrlToFile(imageUrl, convertType) {
322
+ if (convertType === "fetch") {
325
323
  return await imageUrlToFileFetch(imageUrl);
326
324
  }
327
- if (fileConvertType === "xhr") {
325
+ if (convertType === "xhr") {
328
326
  return await imageUrlToFileWithXMLHttpRequest(imageUrl);
329
327
  }
330
328
  return await imageUrlToFileWithCanvas(imageUrl);
331
329
  }
332
330
  async function imageUrlToFileWithCanvas(imageUrl) {
333
331
  const newImage = new Image();
334
- newImage.src = imageUrl;
335
- newImage.crossOrigin = "Anonymous";
336
332
  return new Promise((resolve, reject) => {
337
333
  newImage.onload = () => {
338
334
  const canvas = document.createElement("canvas");
339
335
  canvas.width = newImage.width;
340
336
  canvas.height = newImage.height;
341
337
  const ctx = canvas.getContext("2d");
342
- if (ctx) {
338
+ if (!ctx) {
339
+ reject(new Error("Failed to get canvas 2d context."));
340
+ return;
341
+ }
342
+ try {
343
343
  ctx.drawImage(newImage, 0, 0);
344
- canvas.toBlob((blob) => {
345
- if (blob) {
346
- const ext = getExt(imageUrl);
347
- const file = new File([blob], `image.${ext}`, { type: blob.type });
348
- resolve(file);
349
- }
350
- });
344
+ const dataUrl = canvas.toDataURL();
345
+ const blob = base64ToBlob(dataUrl);
346
+ const fileExt = getExt(blob) ?? "png";
347
+ resolve(blobToFile(blob, `image.${fileExt}`));
348
+ } catch (error) {
349
+ reject(
350
+ new Error(
351
+ `imageUrlToFileWithCanvas failed for "${imageUrl}". ${error instanceof Error ? error.message : "Unknown error"}`
352
+ )
353
+ );
351
354
  }
352
355
  };
353
356
  newImage.onerror = (e) => {
354
357
  reject(JSON.stringify(e));
355
358
  };
359
+ newImage.crossOrigin = "Anonymous";
360
+ newImage.src = imageUrl;
356
361
  });
357
362
  }
358
363
  async function imageUrlToFileWithXMLHttpRequest(imageUrl) {
@@ -4546,7 +4551,7 @@ function resize(image, options = { ext: "jpeg", filesize: maxImageSize }) {
4546
4551
  });
4547
4552
  }
4548
4553
 
4549
- const genImageId = () => `camera-${Date.now()}-${Math.random()}`;
4554
+ const genImageId$1 = () => `camera-${Date.now()}-${Math.random()}`;
4550
4555
  function useCamera({
4551
4556
  onChange,
4552
4557
  resize: resizeOption = {
@@ -4564,7 +4569,8 @@ function useCamera({
4564
4569
  buttonText,
4565
4570
  initData,
4566
4571
  useNativeCamera = false,
4567
- responseFileType
4572
+ responseFileType,
4573
+ convertType = "canvas"
4568
4574
  } = {}) {
4569
4575
  const convertedInitData = initData?.map((data, index) => ({ ...data, id: String(index + 1) }));
4570
4576
  const [attachedPhotos, setAttachedPhotos] = useState(convertedInitData || []);
@@ -4573,7 +4579,7 @@ function useCamera({
4573
4579
  };
4574
4580
  const imageHandler = async (file) => {
4575
4581
  const newPhoto = {
4576
- id: genImageId(),
4582
+ id: genImageId$1(),
4577
4583
  src: URL.createObjectURL(resizeOption ? await resize(file, resizeOption) : file),
4578
4584
  name: `\uC11C\uB958\uC0AC\uC9C4_${attachedPhotos.length + 1}`
4579
4585
  };
@@ -4588,7 +4594,7 @@ function useCamera({
4588
4594
  };
4589
4595
  const processImage = async (uri) => {
4590
4596
  if (responseFileType === "scheme") {
4591
- const file = await imageUrlToFile(uri);
4597
+ const file = await imageUrlToFile(uri, convertType);
4592
4598
  await imageHandler(file);
4593
4599
  } else {
4594
4600
  const file = await base64ToFile(uri, "image.jpg");
@@ -4633,10 +4639,171 @@ function useCamera({
4633
4639
  const imageIndex = attachedPhotos.findIndex((image) => image.id === imageId);
4634
4640
  if (imageIndex > -1) {
4635
4641
  const item = attachedPhotos.splice(imageIndex, 1);
4636
- item[0] && URL.revokeObjectURL(item[0].src);
4642
+ if (item[0]) {
4643
+ URL.revokeObjectURL(item[0].src);
4644
+ }
4637
4645
  setAttachedPhotos([...attachedPhotos]);
4638
- onDelete && onDelete(imageId);
4646
+ if (onDelete) {
4647
+ onDelete(imageId);
4648
+ }
4649
+ }
4650
+ };
4651
+ const deleteAllImages = () => {
4652
+ attachedPhotos.forEach((image) => {
4653
+ URL.revokeObjectURL(image.src);
4654
+ if (onDelete) {
4655
+ onDelete(image.id);
4656
+ }
4657
+ });
4658
+ setAttachedPhotos([]);
4659
+ };
4660
+ const CameraComponent = () => /* @__PURE__ */ jsx(
4661
+ Attachment,
4662
+ {
4663
+ show: !!show,
4664
+ onAddPhoto: onClick,
4665
+ onRemovePhoto: deleteImage,
4666
+ photos: attachedPhotos,
4667
+ type,
4668
+ buttonText
4639
4669
  }
4670
+ );
4671
+ useEffect(() => {
4672
+ return () => {
4673
+ attachedPhotos.forEach((image) => {
4674
+ URL.revokeObjectURL(image.src);
4675
+ });
4676
+ };
4677
+ }, []);
4678
+ return {
4679
+ onClick,
4680
+ getImage: findImage,
4681
+ deleteImage,
4682
+ deleteAllImages,
4683
+ attachedPhotos,
4684
+ Attachment: CameraComponent,
4685
+ addImage: (data) => {
4686
+ setAttachedPhotos([
4687
+ ...attachedPhotos,
4688
+ ...data.map((item) => {
4689
+ let blobUrl = "";
4690
+ if (item.data instanceof Blob) {
4691
+ blobUrl = URL.createObjectURL(item.data);
4692
+ } else if (typeof item.data === "string") {
4693
+ blobUrl = URL.createObjectURL(base64ToBlob(item.data));
4694
+ }
4695
+ const newPhoto = {
4696
+ id: genImageId$1(),
4697
+ src: blobUrl,
4698
+ name: item.name || `\uC11C\uB958\uC0AC\uC9C4_${attachedPhotos.length + 1}`
4699
+ };
4700
+ return newPhoto;
4701
+ })
4702
+ ]);
4703
+ }
4704
+ };
4705
+ }
4706
+
4707
+ const genImageId = () => `camera-${Date.now()}-${Math.random()}`;
4708
+ function useCameraV2({
4709
+ onChange,
4710
+ resize: resizeOption = {
4711
+ processType: "mixed",
4712
+ resizeRatio: 5,
4713
+ width: 1920,
4714
+ // 300kb
4715
+ filesize: 300 * 1024,
4716
+ ext: "jpeg"
4717
+ },
4718
+ cameraOnly,
4719
+ onDelete,
4720
+ show,
4721
+ type = "multiple",
4722
+ buttonText,
4723
+ initData,
4724
+ useNativeCamera = false,
4725
+ responseFileType,
4726
+ convertType = "canvas"
4727
+ }) {
4728
+ const convertedInitData = initData?.map((data, index) => ({ ...data, id: String(index + 1) }));
4729
+ const [attachedPhotos, setAttachedPhotos] = useState(convertedInitData || []);
4730
+ const findImage = async (imageId) => {
4731
+ const photo = attachedPhotos.find((image) => image.id === imageId);
4732
+ if (!photo) {
4733
+ throw new Error("[use-camera-v2] : Image not found");
4734
+ }
4735
+ const file = await imageUrlToFile(photo?.src, convertType);
4736
+ const convertedPhoto = {
4737
+ ...photo,
4738
+ src: URL.createObjectURL(resizeOption ? await resize(file, resizeOption) : file)
4739
+ };
4740
+ return convertedPhoto;
4741
+ };
4742
+ const imageHandler = async (uri) => {
4743
+ const newPhoto = {
4744
+ id: genImageId(),
4745
+ src: uri,
4746
+ name: `\uC11C\uB958\uC0AC\uC9C4_${attachedPhotos.length + 1}`
4747
+ };
4748
+ if (type === "single") {
4749
+ setAttachedPhotos([newPhoto]);
4750
+ } else {
4751
+ setAttachedPhotos([...attachedPhotos, newPhoto]);
4752
+ }
4753
+ if (onChange) {
4754
+ const file = await imageUrlToFile(uri, convertType);
4755
+ onChange(file);
4756
+ }
4757
+ };
4758
+ const onClick = () => {
4759
+ if (useNativeCamera) {
4760
+ Bridge.native.documentCapture({ responseFileType }).then(async ({ uri }) => {
4761
+ imageHandler(uri);
4762
+ });
4763
+ } else {
4764
+ const input = document.createElement("input");
4765
+ input.type = "file";
4766
+ input.accept = "image/*";
4767
+ if (cameraOnly) {
4768
+ input.capture = "camera";
4769
+ }
4770
+ input.addEventListener("change", async (event) => {
4771
+ const target = event.target;
4772
+ const { files } = target;
4773
+ if (files && files.length > 0) {
4774
+ const file = files[0];
4775
+ if (file) {
4776
+ await imageHandler(URL.createObjectURL(file));
4777
+ }
4778
+ }
4779
+ document.body.removeChild(input);
4780
+ });
4781
+ input.style.display = "none";
4782
+ document.body.appendChild(input);
4783
+ input.click();
4784
+ }
4785
+ };
4786
+ const deleteImage = (imageId) => {
4787
+ const imageIndex = attachedPhotos.findIndex((image) => image.id === imageId);
4788
+ if (imageIndex > -1) {
4789
+ const item = attachedPhotos.splice(imageIndex, 1);
4790
+ if (item[0]) {
4791
+ URL.revokeObjectURL(item[0].src);
4792
+ }
4793
+ setAttachedPhotos([...attachedPhotos]);
4794
+ if (onDelete) {
4795
+ onDelete(imageId);
4796
+ }
4797
+ }
4798
+ };
4799
+ const deleteAllImages = () => {
4800
+ attachedPhotos.forEach((image) => {
4801
+ URL.revokeObjectURL(image.src);
4802
+ if (onDelete) {
4803
+ onDelete(image.id);
4804
+ }
4805
+ });
4806
+ setAttachedPhotos([]);
4640
4807
  };
4641
4808
  const CameraComponent = () => /* @__PURE__ */ jsx(
4642
4809
  Attachment,
@@ -4660,6 +4827,7 @@ function useCamera({
4660
4827
  onClick,
4661
4828
  getImage: findImage,
4662
4829
  deleteImage,
4830
+ deleteAllImages,
4663
4831
  attachedPhotos,
4664
4832
  Attachment: CameraComponent,
4665
4833
  addImage: (data) => {
@@ -7071,4 +7239,4 @@ function useTerms(initialValue) {
7071
7239
  };
7072
7240
  }
7073
7241
 
7074
- export { AUTH_TEMPLATE_CODES, Attachment, BANK_STOCK_ICON_LIST, BANK_STOCK_SEARCH_MODAL_TABS, BankStockSearchModal, CustomerSearch, CustomerSearchModal, DeaCustomerSearchModal, DudDownload, DudUpload, EmployeeSearchModal, GtmIframe, HookFormCheckbox, HookFormCheckboxButton, HookFormDatePickerRenew, HookFormDateRangePickerRenew, HookFormSearchJobField, HookFormSegmentGroup, HookFormSelect, HookFormTextField, JobVehicleSearchModal, OrganizationSearchModal, RATING_DATA, RIV_SEARCH_PARAM_MAP, RivModalIframe, StepIndicator, TermsCancerCollectQR, TermsCancerMarketing, TermsCancerProvideQR, TermsCancerSystem, TermsCheckboxButton, TermsDesign, TermsExecution, TermsLoan, TermsMarketing, TermsMarketingCollectQR, TermsMarketingProviderQR, TermsMobileCard, TermsRadio, TermsRatingBar, TermsSignature, TermsTransfer, VERIFICATION_CODES, highlightOnSearchKeyword, resize, testSignatureBase64Data, useAddressComponent, useBankStockSearch, useCamera, useCanvasPaint, useCustomerSearch, useDownloader, useJobSearchModal, useJobVehicleSearch, useJobVehicleSearchModal, useNationalityComponent, useNxlOneModal, useRemoteIdentityVerification, useRemoteIdentityVerificationIframe, useSearchAddress, useSearchNationality, useSearchVisa, useTerms, useVisaComponent };
7242
+ export { AUTH_TEMPLATE_CODES, Attachment, BANK_STOCK_ICON_LIST, BANK_STOCK_SEARCH_MODAL_TABS, BankStockSearchModal, CustomerSearch, CustomerSearchModal, DeaCustomerSearchModal, DudDownload, DudUpload, EmployeeSearchModal, GtmIframe, HookFormCheckbox, HookFormCheckboxButton, HookFormDatePickerRenew, HookFormDateRangePickerRenew, HookFormSearchJobField, HookFormSegmentGroup, HookFormSelect, HookFormTextField, JobVehicleSearchModal, OrganizationSearchModal, RATING_DATA, RIV_SEARCH_PARAM_MAP, RivModalIframe, StepIndicator, TermsCancerCollectQR, TermsCancerMarketing, TermsCancerProvideQR, TermsCancerSystem, TermsCheckboxButton, TermsDesign, TermsExecution, TermsLoan, TermsMarketing, TermsMarketingCollectQR, TermsMarketingProviderQR, TermsMobileCard, TermsRadio, TermsRatingBar, TermsSignature, TermsTransfer, VERIFICATION_CODES, highlightOnSearchKeyword, resize, testSignatureBase64Data, useAddressComponent, useBankStockSearch, useCamera, useCameraV2, useCanvasPaint, useCustomerSearch, useDownloader, useJobSearchModal, useJobVehicleSearch, useJobVehicleSearchModal, useNationalityComponent, useNxlOneModal, useRemoteIdentityVerification, useRemoteIdentityVerificationIframe, useSearchAddress, useSearchNationality, useSearchVisa, useTerms, useVisaComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sales-frontend-components",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
@@ -45,12 +45,12 @@
45
45
  "sales-frontend-stores": "0.0.15",
46
46
  "sales-frontend-typescript-config": "0.0.2",
47
47
  "sales-frontend-assets": "0.0.27",
48
- "sales-frontend-api": "0.0.178",
49
- "sales-frontend-design-system": "0.2.2",
50
- "sales-frontend-bridge": "0.0.118",
51
- "sales-frontend-hooks": "0.0.179",
52
- "sales-frontend-solution": "0.0.58",
53
- "sales-frontend-debug": "0.0.76",
48
+ "sales-frontend-api": "0.0.180",
49
+ "sales-frontend-design-system": "0.2.4",
50
+ "sales-frontend-bridge": "0.0.120",
51
+ "sales-frontend-hooks": "0.0.181",
52
+ "sales-frontend-solution": "0.0.60",
53
+ "sales-frontend-debug": "0.0.78",
54
54
  "sales-frontend-vitest-config": "0.0.3"
55
55
  },
56
56
  "peerDependencies": {
@@ -58,19 +58,19 @@
58
58
  "react": ">=19.0.0",
59
59
  "react-dom": ">=19.0.0",
60
60
  "react-hook-form": "^7.58.1",
61
- "sales-frontend-api": "0.0.178",
61
+ "sales-frontend-api": "0.0.180",
62
62
  "sales-frontend-assets": "0.0.27",
63
63
  "sales-frontend-stores": "0.0.15",
64
- "sales-frontend-design-system": "0.2.2",
65
- "sales-frontend-bridge": "0.0.118",
66
- "sales-frontend-hooks": "0.0.179",
67
- "sales-frontend-solution": "0.0.58"
64
+ "sales-frontend-design-system": "0.2.4",
65
+ "sales-frontend-bridge": "0.0.120",
66
+ "sales-frontend-hooks": "0.0.181",
67
+ "sales-frontend-solution": "0.0.60"
68
68
  },
69
69
  "dependencies": {
70
70
  "classnames": "^2.5.1",
71
71
  "dayjs": "^1.11.13",
72
- "sales-frontend-utils": "0.0.70",
73
- "sales-frontend-debug": "0.0.76"
72
+ "sales-frontend-utils": "0.0.72",
73
+ "sales-frontend-debug": "0.0.78"
74
74
  },
75
75
  "scripts": {
76
76
  "lint": "eslint . --max-warnings 0",
@@ -1,25 +0,0 @@
1
- .container {
2
- display: flex;
3
- flex-direction: column;
4
- gap: 8px;
5
- padding: 16px;
6
- }
7
-
8
- .title {
9
- font-size: 16px;
10
- font-weight: 600;
11
- }
12
-
13
- .count {
14
- font-size: 14px;
15
- color: #666;
16
- }
17
-
18
- .button {
19
- padding: 8px 16px;
20
- color: #fff;
21
- cursor: pointer;
22
- background-color: #007bff;
23
- border: none;
24
- border-radius: 4px;
25
- }