@fctc/widget-logic 3.10.5 → 3.10.6

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.js CHANGED
@@ -6194,12 +6194,33 @@ var colorFieldController = (props) => {
6194
6194
  // src/widget/basic/binary-field/controller.ts
6195
6195
  var import_react22 = require("react");
6196
6196
  var import_utils16 = require("@fctc/interface-logic/utils");
6197
+ var ALLOWED_TYPES = [
6198
+ "image/jpeg",
6199
+ "image/png",
6200
+ "application/pdf",
6201
+ "video/mp4",
6202
+ "application/zip",
6203
+ "application/x-zip-compressed",
6204
+ "application/vnd.ms-excel",
6205
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
6206
+ "application/json"
6207
+ ];
6208
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
6209
+ var MAX_TOTAL_SIZE = 50 * 1024 * 1024;
6197
6210
  var binaryFieldController = (props) => {
6198
- const { name, methods, readonly = false, value } = props;
6211
+ const {
6212
+ name,
6213
+ methods,
6214
+ readonly = false,
6215
+ value,
6216
+ filename,
6217
+ rootField,
6218
+ index
6219
+ } = props;
6199
6220
  const inputId = (0, import_react22.useId)();
6200
- const [selectedImage, setSelectedImage] = (0, import_react22.useState)(null);
6201
- const [initialImage, setInitialImage] = (0, import_react22.useState)(value || null);
6202
- const [isInsideTable, setIsInsideTable] = (0, import_react22.useState)(false);
6221
+ const [selectedFile, setSelectedFile] = (0, import_react22.useState)(null);
6222
+ const [selectedPreview, setSelectedPreview] = (0, import_react22.useState)(null);
6223
+ const [initialFile, setInitialFile] = (0, import_react22.useState)(value || null);
6203
6224
  const { setValue } = methods;
6204
6225
  const binaryRef = (0, import_react22.useRef)(null);
6205
6226
  const convertUrlToBase64 = async (url) => {
@@ -6208,9 +6229,7 @@ var binaryFieldController = (props) => {
6208
6229
  const blob = await response.blob();
6209
6230
  return new Promise((resolve, reject) => {
6210
6231
  const reader = new FileReader();
6211
- reader.onloadend = () => {
6212
- resolve(reader.result);
6213
- };
6232
+ reader.onloadend = () => resolve(reader.result);
6214
6233
  reader.onerror = reject;
6215
6234
  reader.readAsDataURL(blob);
6216
6235
  });
@@ -6219,40 +6238,13 @@ var binaryFieldController = (props) => {
6219
6238
  throw error;
6220
6239
  }
6221
6240
  };
6222
- const extractBase64Data = (base64Url) => {
6223
- if (base64Url.includes("base64,")) {
6224
- return base64Url.split("base64,")[1];
6225
- }
6226
- return base64Url;
6227
- };
6228
- const handleImageChange = async (e, onChange) => {
6229
- if (readonly) return;
6230
- const file = e?.target?.files?.[0];
6231
- if (file) {
6232
- const imageUrl = URL.createObjectURL(file);
6233
- setSelectedImage(imageUrl);
6234
- setInitialImage(null);
6235
- onChange(file);
6236
- const compressedBase64 = await convertUrlToBase64(imageUrl);
6237
- const base64Data = extractBase64Data(compressedBase64);
6238
- setValue(name, base64Data, {
6239
- shouldDirty: true
6240
- });
6241
- }
6242
- };
6243
- const handleRemoveImage = (onChange) => {
6244
- setSelectedImage(null);
6245
- setInitialImage(null);
6246
- onChange(null);
6247
- };
6248
- const isBlobUrl = (url) => {
6249
- return /^blob:/.test(url);
6250
- };
6241
+ const extractBase64Data = (base64Url) => base64Url.includes("base64,") ? base64Url.split("base64,")[1] : base64Url;
6242
+ const isBlobUrl = (url) => /^blob:/.test(url);
6251
6243
  const checkIsImageLink = (url) => {
6252
6244
  const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
6253
6245
  return imageExtensions.test(url) || (0, import_utils16.isBase64Image)(url) || isBlobUrl(url);
6254
6246
  };
6255
- const getImageBase64WithMimeType = (base64) => {
6247
+ const getBase64WithMimeType = (base64) => {
6256
6248
  if (typeof base64 !== "string" || base64.length < 10) return null;
6257
6249
  if ((0, import_utils16.isBase64Image)(base64)) return base64;
6258
6250
  let mimeType = null;
@@ -6263,29 +6255,62 @@ var binaryFieldController = (props) => {
6263
6255
  else if (base64.startsWith("UklGR")) mimeType = "image/webp";
6264
6256
  return mimeType ? `data:${mimeType};base64,${base64}` : null;
6265
6257
  };
6258
+ const handleFileChange = async (e, onChange) => {
6259
+ if (readonly) return;
6260
+ const file = e?.target?.files?.[0];
6261
+ if (!file) return;
6262
+ if (!ALLOWED_TYPES.includes(file.type)) {
6263
+ alert(`File type not allowed: ${file.type}`);
6264
+ return;
6265
+ }
6266
+ if (file.size > MAX_FILE_SIZE) {
6267
+ alert(`File exceeds 10MB limit.`);
6268
+ return;
6269
+ }
6270
+ const fileUrl = URL.createObjectURL(file);
6271
+ setSelectedFile(file);
6272
+ setInitialFile(null);
6273
+ setSelectedPreview(fileUrl);
6274
+ onChange(file);
6275
+ if (file.type.startsWith("image/") || file.type === "application/json") {
6276
+ const base64 = await convertUrlToBase64(fileUrl);
6277
+ const base64Data = extractBase64Data(base64);
6278
+ setValue(name, base64Data, { shouldDirty: true });
6279
+ } else {
6280
+ setValue(name, file, { shouldDirty: true });
6281
+ }
6282
+ if (filename) {
6283
+ setValue(
6284
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
6285
+ file?.name,
6286
+ { shouldDirty: true }
6287
+ );
6288
+ }
6289
+ };
6290
+ const handleRemoveFile = (onChange) => {
6291
+ if (selectedPreview) URL.revokeObjectURL(selectedPreview);
6292
+ setSelectedFile(null);
6293
+ setSelectedPreview(null);
6294
+ setInitialFile(null);
6295
+ onChange(null);
6296
+ };
6266
6297
  (0, import_react22.useEffect)(() => {
6267
6298
  return () => {
6268
- if (selectedImage) {
6269
- URL.revokeObjectURL(selectedImage);
6299
+ if (selectedPreview) {
6300
+ URL.revokeObjectURL(selectedPreview);
6270
6301
  }
6271
6302
  };
6272
- }, [selectedImage]);
6273
- (0, import_react22.useEffect)(() => {
6274
- if (binaryRef.current) {
6275
- const isInsideTable2 = !!binaryRef.current.closest("table");
6276
- setIsInsideTable(isInsideTable2);
6277
- }
6278
- }, []);
6303
+ }, [selectedPreview]);
6279
6304
  return {
6280
6305
  inputId,
6281
- selectedImage,
6282
- initialImage,
6283
- isInsideTable,
6306
+ selectedFile,
6307
+ selectedPreview,
6308
+ initialFile,
6284
6309
  binaryRef,
6285
- handleImageChange,
6286
- handleRemoveImage,
6310
+ handleFileChange,
6311
+ handleRemoveFile,
6287
6312
  checkIsImageLink,
6288
- getImageBase64WithMimeType
6313
+ getBase64WithMimeType
6289
6314
  };
6290
6315
  };
6291
6316
 
package/dist/index.mjs CHANGED
@@ -6274,12 +6274,33 @@ var colorFieldController = (props) => {
6274
6274
  // src/widget/basic/binary-field/controller.ts
6275
6275
  import { useEffect as useEffect13, useId as useId2, useRef as useRef4, useState as useState12 } from "react";
6276
6276
  import { isBase64Image } from "@fctc/interface-logic/utils";
6277
+ var ALLOWED_TYPES = [
6278
+ "image/jpeg",
6279
+ "image/png",
6280
+ "application/pdf",
6281
+ "video/mp4",
6282
+ "application/zip",
6283
+ "application/x-zip-compressed",
6284
+ "application/vnd.ms-excel",
6285
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
6286
+ "application/json"
6287
+ ];
6288
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
6289
+ var MAX_TOTAL_SIZE = 50 * 1024 * 1024;
6277
6290
  var binaryFieldController = (props) => {
6278
- const { name, methods, readonly = false, value } = props;
6291
+ const {
6292
+ name,
6293
+ methods,
6294
+ readonly = false,
6295
+ value,
6296
+ filename,
6297
+ rootField,
6298
+ index
6299
+ } = props;
6279
6300
  const inputId = useId2();
6280
- const [selectedImage, setSelectedImage] = useState12(null);
6281
- const [initialImage, setInitialImage] = useState12(value || null);
6282
- const [isInsideTable, setIsInsideTable] = useState12(false);
6301
+ const [selectedFile, setSelectedFile] = useState12(null);
6302
+ const [selectedPreview, setSelectedPreview] = useState12(null);
6303
+ const [initialFile, setInitialFile] = useState12(value || null);
6283
6304
  const { setValue } = methods;
6284
6305
  const binaryRef = useRef4(null);
6285
6306
  const convertUrlToBase64 = async (url) => {
@@ -6288,9 +6309,7 @@ var binaryFieldController = (props) => {
6288
6309
  const blob = await response.blob();
6289
6310
  return new Promise((resolve, reject) => {
6290
6311
  const reader = new FileReader();
6291
- reader.onloadend = () => {
6292
- resolve(reader.result);
6293
- };
6312
+ reader.onloadend = () => resolve(reader.result);
6294
6313
  reader.onerror = reject;
6295
6314
  reader.readAsDataURL(blob);
6296
6315
  });
@@ -6299,40 +6318,13 @@ var binaryFieldController = (props) => {
6299
6318
  throw error;
6300
6319
  }
6301
6320
  };
6302
- const extractBase64Data = (base64Url) => {
6303
- if (base64Url.includes("base64,")) {
6304
- return base64Url.split("base64,")[1];
6305
- }
6306
- return base64Url;
6307
- };
6308
- const handleImageChange = async (e, onChange) => {
6309
- if (readonly) return;
6310
- const file = e?.target?.files?.[0];
6311
- if (file) {
6312
- const imageUrl = URL.createObjectURL(file);
6313
- setSelectedImage(imageUrl);
6314
- setInitialImage(null);
6315
- onChange(file);
6316
- const compressedBase64 = await convertUrlToBase64(imageUrl);
6317
- const base64Data = extractBase64Data(compressedBase64);
6318
- setValue(name, base64Data, {
6319
- shouldDirty: true
6320
- });
6321
- }
6322
- };
6323
- const handleRemoveImage = (onChange) => {
6324
- setSelectedImage(null);
6325
- setInitialImage(null);
6326
- onChange(null);
6327
- };
6328
- const isBlobUrl = (url) => {
6329
- return /^blob:/.test(url);
6330
- };
6321
+ const extractBase64Data = (base64Url) => base64Url.includes("base64,") ? base64Url.split("base64,")[1] : base64Url;
6322
+ const isBlobUrl = (url) => /^blob:/.test(url);
6331
6323
  const checkIsImageLink = (url) => {
6332
6324
  const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
6333
6325
  return imageExtensions.test(url) || isBase64Image(url) || isBlobUrl(url);
6334
6326
  };
6335
- const getImageBase64WithMimeType = (base64) => {
6327
+ const getBase64WithMimeType = (base64) => {
6336
6328
  if (typeof base64 !== "string" || base64.length < 10) return null;
6337
6329
  if (isBase64Image(base64)) return base64;
6338
6330
  let mimeType = null;
@@ -6343,29 +6335,62 @@ var binaryFieldController = (props) => {
6343
6335
  else if (base64.startsWith("UklGR")) mimeType = "image/webp";
6344
6336
  return mimeType ? `data:${mimeType};base64,${base64}` : null;
6345
6337
  };
6338
+ const handleFileChange = async (e, onChange) => {
6339
+ if (readonly) return;
6340
+ const file = e?.target?.files?.[0];
6341
+ if (!file) return;
6342
+ if (!ALLOWED_TYPES.includes(file.type)) {
6343
+ alert(`File type not allowed: ${file.type}`);
6344
+ return;
6345
+ }
6346
+ if (file.size > MAX_FILE_SIZE) {
6347
+ alert(`File exceeds 10MB limit.`);
6348
+ return;
6349
+ }
6350
+ const fileUrl = URL.createObjectURL(file);
6351
+ setSelectedFile(file);
6352
+ setInitialFile(null);
6353
+ setSelectedPreview(fileUrl);
6354
+ onChange(file);
6355
+ if (file.type.startsWith("image/") || file.type === "application/json") {
6356
+ const base64 = await convertUrlToBase64(fileUrl);
6357
+ const base64Data = extractBase64Data(base64);
6358
+ setValue(name, base64Data, { shouldDirty: true });
6359
+ } else {
6360
+ setValue(name, file, { shouldDirty: true });
6361
+ }
6362
+ if (filename) {
6363
+ setValue(
6364
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
6365
+ file?.name,
6366
+ { shouldDirty: true }
6367
+ );
6368
+ }
6369
+ };
6370
+ const handleRemoveFile = (onChange) => {
6371
+ if (selectedPreview) URL.revokeObjectURL(selectedPreview);
6372
+ setSelectedFile(null);
6373
+ setSelectedPreview(null);
6374
+ setInitialFile(null);
6375
+ onChange(null);
6376
+ };
6346
6377
  useEffect13(() => {
6347
6378
  return () => {
6348
- if (selectedImage) {
6349
- URL.revokeObjectURL(selectedImage);
6379
+ if (selectedPreview) {
6380
+ URL.revokeObjectURL(selectedPreview);
6350
6381
  }
6351
6382
  };
6352
- }, [selectedImage]);
6353
- useEffect13(() => {
6354
- if (binaryRef.current) {
6355
- const isInsideTable2 = !!binaryRef.current.closest("table");
6356
- setIsInsideTable(isInsideTable2);
6357
- }
6358
- }, []);
6383
+ }, [selectedPreview]);
6359
6384
  return {
6360
6385
  inputId,
6361
- selectedImage,
6362
- initialImage,
6363
- isInsideTable,
6386
+ selectedFile,
6387
+ selectedPreview,
6388
+ initialFile,
6364
6389
  binaryRef,
6365
- handleImageChange,
6366
- handleRemoveImage,
6390
+ handleFileChange,
6391
+ handleRemoveFile,
6367
6392
  checkIsImageLink,
6368
- getImageBase64WithMimeType
6393
+ getBase64WithMimeType
6369
6394
  };
6370
6395
  };
6371
6396
 
package/dist/types.d.mts CHANGED
@@ -30,6 +30,7 @@ interface IInputFieldProps {
30
30
  widget?: string;
31
31
  aid?: string | number;
32
32
  context?: string | Object;
33
+ filename?: string;
33
34
  }
34
35
 
35
36
  export type { IInputFieldProps, ValuePropsType };
package/dist/types.d.ts CHANGED
@@ -30,6 +30,7 @@ interface IInputFieldProps {
30
30
  widget?: string;
31
31
  aid?: string | number;
32
32
  context?: string | Object;
33
+ filename?: string;
33
34
  }
34
35
 
35
36
  export type { IInputFieldProps, ValuePropsType };
package/dist/widget.d.mts CHANGED
@@ -208,16 +208,19 @@ declare const colorFieldController: (props: IColorFieldProps) => {
208
208
  savePickColor: (colorObject: any) => Promise<void>;
209
209
  };
210
210
 
211
- declare const binaryFieldController: (props: IInputFieldProps) => {
211
+ declare const binaryFieldController: (props: IInputFieldProps & {
212
+ rootField?: any;
213
+ index?: number;
214
+ }) => {
212
215
  inputId: string;
213
- selectedImage: string | null;
214
- initialImage: any;
215
- isInsideTable: boolean;
216
+ selectedFile: File | null;
217
+ selectedPreview: string | null;
218
+ initialFile: any;
216
219
  binaryRef: react.RefObject<HTMLDivElement>;
217
- handleImageChange: (e: ChangeEvent<HTMLInputElement>, onChange: (file: File | null) => void) => Promise<void>;
218
- handleRemoveImage: (onChange: (file: File | null) => void) => void;
220
+ handleFileChange: (e: ChangeEvent<HTMLInputElement>, onChange: (file: File | null) => void) => Promise<void>;
221
+ handleRemoveFile: (onChange: (file: File | null) => void) => void;
219
222
  checkIsImageLink: (url: string) => boolean;
220
- getImageBase64WithMimeType: (base64: any) => string | null;
223
+ getBase64WithMimeType: (base64: string) => string | null;
221
224
  };
222
225
 
223
226
  declare const tableHeadController: (props: any) => {
package/dist/widget.d.ts CHANGED
@@ -208,16 +208,19 @@ declare const colorFieldController: (props: IColorFieldProps) => {
208
208
  savePickColor: (colorObject: any) => Promise<void>;
209
209
  };
210
210
 
211
- declare const binaryFieldController: (props: IInputFieldProps) => {
211
+ declare const binaryFieldController: (props: IInputFieldProps & {
212
+ rootField?: any;
213
+ index?: number;
214
+ }) => {
212
215
  inputId: string;
213
- selectedImage: string | null;
214
- initialImage: any;
215
- isInsideTable: boolean;
216
+ selectedFile: File | null;
217
+ selectedPreview: string | null;
218
+ initialFile: any;
216
219
  binaryRef: react.RefObject<HTMLDivElement>;
217
- handleImageChange: (e: ChangeEvent<HTMLInputElement>, onChange: (file: File | null) => void) => Promise<void>;
218
- handleRemoveImage: (onChange: (file: File | null) => void) => void;
220
+ handleFileChange: (e: ChangeEvent<HTMLInputElement>, onChange: (file: File | null) => void) => Promise<void>;
221
+ handleRemoveFile: (onChange: (file: File | null) => void) => void;
219
222
  checkIsImageLink: (url: string) => boolean;
220
- getImageBase64WithMimeType: (base64: any) => string | null;
223
+ getBase64WithMimeType: (base64: string) => string | null;
221
224
  };
222
225
 
223
226
  declare const tableHeadController: (props: any) => {
package/dist/widget.js CHANGED
@@ -5431,12 +5431,33 @@ var colorFieldController = (props) => {
5431
5431
  // src/widget/basic/binary-field/controller.ts
5432
5432
  var import_react22 = require("react");
5433
5433
  var import_utils16 = require("@fctc/interface-logic/utils");
5434
+ var ALLOWED_TYPES = [
5435
+ "image/jpeg",
5436
+ "image/png",
5437
+ "application/pdf",
5438
+ "video/mp4",
5439
+ "application/zip",
5440
+ "application/x-zip-compressed",
5441
+ "application/vnd.ms-excel",
5442
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
5443
+ "application/json"
5444
+ ];
5445
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
5446
+ var MAX_TOTAL_SIZE = 50 * 1024 * 1024;
5434
5447
  var binaryFieldController = (props) => {
5435
- const { name, methods, readonly = false, value } = props;
5448
+ const {
5449
+ name,
5450
+ methods,
5451
+ readonly = false,
5452
+ value,
5453
+ filename,
5454
+ rootField,
5455
+ index
5456
+ } = props;
5436
5457
  const inputId = (0, import_react22.useId)();
5437
- const [selectedImage, setSelectedImage] = (0, import_react22.useState)(null);
5438
- const [initialImage, setInitialImage] = (0, import_react22.useState)(value || null);
5439
- const [isInsideTable, setIsInsideTable] = (0, import_react22.useState)(false);
5458
+ const [selectedFile, setSelectedFile] = (0, import_react22.useState)(null);
5459
+ const [selectedPreview, setSelectedPreview] = (0, import_react22.useState)(null);
5460
+ const [initialFile, setInitialFile] = (0, import_react22.useState)(value || null);
5440
5461
  const { setValue } = methods;
5441
5462
  const binaryRef = (0, import_react22.useRef)(null);
5442
5463
  const convertUrlToBase64 = async (url) => {
@@ -5445,9 +5466,7 @@ var binaryFieldController = (props) => {
5445
5466
  const blob = await response.blob();
5446
5467
  return new Promise((resolve, reject) => {
5447
5468
  const reader = new FileReader();
5448
- reader.onloadend = () => {
5449
- resolve(reader.result);
5450
- };
5469
+ reader.onloadend = () => resolve(reader.result);
5451
5470
  reader.onerror = reject;
5452
5471
  reader.readAsDataURL(blob);
5453
5472
  });
@@ -5456,40 +5475,13 @@ var binaryFieldController = (props) => {
5456
5475
  throw error;
5457
5476
  }
5458
5477
  };
5459
- const extractBase64Data = (base64Url) => {
5460
- if (base64Url.includes("base64,")) {
5461
- return base64Url.split("base64,")[1];
5462
- }
5463
- return base64Url;
5464
- };
5465
- const handleImageChange = async (e, onChange) => {
5466
- if (readonly) return;
5467
- const file = e?.target?.files?.[0];
5468
- if (file) {
5469
- const imageUrl = URL.createObjectURL(file);
5470
- setSelectedImage(imageUrl);
5471
- setInitialImage(null);
5472
- onChange(file);
5473
- const compressedBase64 = await convertUrlToBase64(imageUrl);
5474
- const base64Data = extractBase64Data(compressedBase64);
5475
- setValue(name, base64Data, {
5476
- shouldDirty: true
5477
- });
5478
- }
5479
- };
5480
- const handleRemoveImage = (onChange) => {
5481
- setSelectedImage(null);
5482
- setInitialImage(null);
5483
- onChange(null);
5484
- };
5485
- const isBlobUrl = (url) => {
5486
- return /^blob:/.test(url);
5487
- };
5478
+ const extractBase64Data = (base64Url) => base64Url.includes("base64,") ? base64Url.split("base64,")[1] : base64Url;
5479
+ const isBlobUrl = (url) => /^blob:/.test(url);
5488
5480
  const checkIsImageLink = (url) => {
5489
5481
  const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
5490
5482
  return imageExtensions.test(url) || (0, import_utils16.isBase64Image)(url) || isBlobUrl(url);
5491
5483
  };
5492
- const getImageBase64WithMimeType = (base64) => {
5484
+ const getBase64WithMimeType = (base64) => {
5493
5485
  if (typeof base64 !== "string" || base64.length < 10) return null;
5494
5486
  if ((0, import_utils16.isBase64Image)(base64)) return base64;
5495
5487
  let mimeType = null;
@@ -5500,29 +5492,62 @@ var binaryFieldController = (props) => {
5500
5492
  else if (base64.startsWith("UklGR")) mimeType = "image/webp";
5501
5493
  return mimeType ? `data:${mimeType};base64,${base64}` : null;
5502
5494
  };
5495
+ const handleFileChange = async (e, onChange) => {
5496
+ if (readonly) return;
5497
+ const file = e?.target?.files?.[0];
5498
+ if (!file) return;
5499
+ if (!ALLOWED_TYPES.includes(file.type)) {
5500
+ alert(`File type not allowed: ${file.type}`);
5501
+ return;
5502
+ }
5503
+ if (file.size > MAX_FILE_SIZE) {
5504
+ alert(`File exceeds 10MB limit.`);
5505
+ return;
5506
+ }
5507
+ const fileUrl = URL.createObjectURL(file);
5508
+ setSelectedFile(file);
5509
+ setInitialFile(null);
5510
+ setSelectedPreview(fileUrl);
5511
+ onChange(file);
5512
+ if (file.type.startsWith("image/") || file.type === "application/json") {
5513
+ const base64 = await convertUrlToBase64(fileUrl);
5514
+ const base64Data = extractBase64Data(base64);
5515
+ setValue(name, base64Data, { shouldDirty: true });
5516
+ } else {
5517
+ setValue(name, file, { shouldDirty: true });
5518
+ }
5519
+ if (filename) {
5520
+ setValue(
5521
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
5522
+ file?.name,
5523
+ { shouldDirty: true }
5524
+ );
5525
+ }
5526
+ };
5527
+ const handleRemoveFile = (onChange) => {
5528
+ if (selectedPreview) URL.revokeObjectURL(selectedPreview);
5529
+ setSelectedFile(null);
5530
+ setSelectedPreview(null);
5531
+ setInitialFile(null);
5532
+ onChange(null);
5533
+ };
5503
5534
  (0, import_react22.useEffect)(() => {
5504
5535
  return () => {
5505
- if (selectedImage) {
5506
- URL.revokeObjectURL(selectedImage);
5536
+ if (selectedPreview) {
5537
+ URL.revokeObjectURL(selectedPreview);
5507
5538
  }
5508
5539
  };
5509
- }, [selectedImage]);
5510
- (0, import_react22.useEffect)(() => {
5511
- if (binaryRef.current) {
5512
- const isInsideTable2 = !!binaryRef.current.closest("table");
5513
- setIsInsideTable(isInsideTable2);
5514
- }
5515
- }, []);
5540
+ }, [selectedPreview]);
5516
5541
  return {
5517
5542
  inputId,
5518
- selectedImage,
5519
- initialImage,
5520
- isInsideTable,
5543
+ selectedFile,
5544
+ selectedPreview,
5545
+ initialFile,
5521
5546
  binaryRef,
5522
- handleImageChange,
5523
- handleRemoveImage,
5547
+ handleFileChange,
5548
+ handleRemoveFile,
5524
5549
  checkIsImageLink,
5525
- getImageBase64WithMimeType
5550
+ getBase64WithMimeType
5526
5551
  };
5527
5552
  };
5528
5553
 
package/dist/widget.mjs CHANGED
@@ -5488,12 +5488,33 @@ var colorFieldController = (props) => {
5488
5488
  // src/widget/basic/binary-field/controller.ts
5489
5489
  import { useEffect as useEffect13, useId as useId2, useRef as useRef4, useState as useState12 } from "react";
5490
5490
  import { isBase64Image } from "@fctc/interface-logic/utils";
5491
+ var ALLOWED_TYPES = [
5492
+ "image/jpeg",
5493
+ "image/png",
5494
+ "application/pdf",
5495
+ "video/mp4",
5496
+ "application/zip",
5497
+ "application/x-zip-compressed",
5498
+ "application/vnd.ms-excel",
5499
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
5500
+ "application/json"
5501
+ ];
5502
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
5503
+ var MAX_TOTAL_SIZE = 50 * 1024 * 1024;
5491
5504
  var binaryFieldController = (props) => {
5492
- const { name, methods, readonly = false, value } = props;
5505
+ const {
5506
+ name,
5507
+ methods,
5508
+ readonly = false,
5509
+ value,
5510
+ filename,
5511
+ rootField,
5512
+ index
5513
+ } = props;
5493
5514
  const inputId = useId2();
5494
- const [selectedImage, setSelectedImage] = useState12(null);
5495
- const [initialImage, setInitialImage] = useState12(value || null);
5496
- const [isInsideTable, setIsInsideTable] = useState12(false);
5515
+ const [selectedFile, setSelectedFile] = useState12(null);
5516
+ const [selectedPreview, setSelectedPreview] = useState12(null);
5517
+ const [initialFile, setInitialFile] = useState12(value || null);
5497
5518
  const { setValue } = methods;
5498
5519
  const binaryRef = useRef4(null);
5499
5520
  const convertUrlToBase64 = async (url) => {
@@ -5502,9 +5523,7 @@ var binaryFieldController = (props) => {
5502
5523
  const blob = await response.blob();
5503
5524
  return new Promise((resolve, reject) => {
5504
5525
  const reader = new FileReader();
5505
- reader.onloadend = () => {
5506
- resolve(reader.result);
5507
- };
5526
+ reader.onloadend = () => resolve(reader.result);
5508
5527
  reader.onerror = reject;
5509
5528
  reader.readAsDataURL(blob);
5510
5529
  });
@@ -5513,40 +5532,13 @@ var binaryFieldController = (props) => {
5513
5532
  throw error;
5514
5533
  }
5515
5534
  };
5516
- const extractBase64Data = (base64Url) => {
5517
- if (base64Url.includes("base64,")) {
5518
- return base64Url.split("base64,")[1];
5519
- }
5520
- return base64Url;
5521
- };
5522
- const handleImageChange = async (e, onChange) => {
5523
- if (readonly) return;
5524
- const file = e?.target?.files?.[0];
5525
- if (file) {
5526
- const imageUrl = URL.createObjectURL(file);
5527
- setSelectedImage(imageUrl);
5528
- setInitialImage(null);
5529
- onChange(file);
5530
- const compressedBase64 = await convertUrlToBase64(imageUrl);
5531
- const base64Data = extractBase64Data(compressedBase64);
5532
- setValue(name, base64Data, {
5533
- shouldDirty: true
5534
- });
5535
- }
5536
- };
5537
- const handleRemoveImage = (onChange) => {
5538
- setSelectedImage(null);
5539
- setInitialImage(null);
5540
- onChange(null);
5541
- };
5542
- const isBlobUrl = (url) => {
5543
- return /^blob:/.test(url);
5544
- };
5535
+ const extractBase64Data = (base64Url) => base64Url.includes("base64,") ? base64Url.split("base64,")[1] : base64Url;
5536
+ const isBlobUrl = (url) => /^blob:/.test(url);
5545
5537
  const checkIsImageLink = (url) => {
5546
5538
  const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
5547
5539
  return imageExtensions.test(url) || isBase64Image(url) || isBlobUrl(url);
5548
5540
  };
5549
- const getImageBase64WithMimeType = (base64) => {
5541
+ const getBase64WithMimeType = (base64) => {
5550
5542
  if (typeof base64 !== "string" || base64.length < 10) return null;
5551
5543
  if (isBase64Image(base64)) return base64;
5552
5544
  let mimeType = null;
@@ -5557,29 +5549,62 @@ var binaryFieldController = (props) => {
5557
5549
  else if (base64.startsWith("UklGR")) mimeType = "image/webp";
5558
5550
  return mimeType ? `data:${mimeType};base64,${base64}` : null;
5559
5551
  };
5552
+ const handleFileChange = async (e, onChange) => {
5553
+ if (readonly) return;
5554
+ const file = e?.target?.files?.[0];
5555
+ if (!file) return;
5556
+ if (!ALLOWED_TYPES.includes(file.type)) {
5557
+ alert(`File type not allowed: ${file.type}`);
5558
+ return;
5559
+ }
5560
+ if (file.size > MAX_FILE_SIZE) {
5561
+ alert(`File exceeds 10MB limit.`);
5562
+ return;
5563
+ }
5564
+ const fileUrl = URL.createObjectURL(file);
5565
+ setSelectedFile(file);
5566
+ setInitialFile(null);
5567
+ setSelectedPreview(fileUrl);
5568
+ onChange(file);
5569
+ if (file.type.startsWith("image/") || file.type === "application/json") {
5570
+ const base64 = await convertUrlToBase64(fileUrl);
5571
+ const base64Data = extractBase64Data(base64);
5572
+ setValue(name, base64Data, { shouldDirty: true });
5573
+ } else {
5574
+ setValue(name, file, { shouldDirty: true });
5575
+ }
5576
+ if (filename) {
5577
+ setValue(
5578
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
5579
+ file?.name,
5580
+ { shouldDirty: true }
5581
+ );
5582
+ }
5583
+ };
5584
+ const handleRemoveFile = (onChange) => {
5585
+ if (selectedPreview) URL.revokeObjectURL(selectedPreview);
5586
+ setSelectedFile(null);
5587
+ setSelectedPreview(null);
5588
+ setInitialFile(null);
5589
+ onChange(null);
5590
+ };
5560
5591
  useEffect13(() => {
5561
5592
  return () => {
5562
- if (selectedImage) {
5563
- URL.revokeObjectURL(selectedImage);
5593
+ if (selectedPreview) {
5594
+ URL.revokeObjectURL(selectedPreview);
5564
5595
  }
5565
5596
  };
5566
- }, [selectedImage]);
5567
- useEffect13(() => {
5568
- if (binaryRef.current) {
5569
- const isInsideTable2 = !!binaryRef.current.closest("table");
5570
- setIsInsideTable(isInsideTable2);
5571
- }
5572
- }, []);
5597
+ }, [selectedPreview]);
5573
5598
  return {
5574
5599
  inputId,
5575
- selectedImage,
5576
- initialImage,
5577
- isInsideTable,
5600
+ selectedFile,
5601
+ selectedPreview,
5602
+ initialFile,
5578
5603
  binaryRef,
5579
- handleImageChange,
5580
- handleRemoveImage,
5604
+ handleFileChange,
5605
+ handleRemoveFile,
5581
5606
  checkIsImageLink,
5582
- getImageBase64WithMimeType
5607
+ getBase64WithMimeType
5583
5608
  };
5584
5609
  };
5585
5610
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fctc/widget-logic",
3
- "version": "3.10.5",
3
+ "version": "3.10.6",
4
4
  "types": "dist/index.d.ts",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",