fui-material 2.8.15 → 2.8.17

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.
@@ -2018,16 +2018,177 @@ const FOpenImgFull = ({
2018
2018
  document.body
2019
2019
  );
2020
2020
  };
2021
- const preview$1 = "_preview_b55p5_1";
2022
- const imageButton = "_imageButton_b55p5_7";
2023
- const download = "_download_b55p5_27";
2021
+ function fDownloadBlobFile(blob, fileName) {
2022
+ if (!blob) {
2023
+ throw new Error("Blob or string content must not be empty");
2024
+ }
2025
+ const blobObject = typeof blob === "string" ? new Blob([blob], { type: "text/plain;charset=utf-8" }) : blob;
2026
+ const link2 = document.createElement("a");
2027
+ const url = window.URL.createObjectURL(blobObject);
2028
+ link2.href = url;
2029
+ link2.download = fileName;
2030
+ link2.style.display = "none";
2031
+ document.body.appendChild(link2);
2032
+ link2.click();
2033
+ document.body.removeChild(link2);
2034
+ window.URL.revokeObjectURL(url);
2035
+ }
2036
+ const fDownloadFileFromBase64 = (base64String, fileName) => {
2037
+ if (!base64String) {
2038
+ console.error("Invalid Base64 string");
2039
+ return;
2040
+ }
2041
+ let mimeType;
2042
+ let base64Data;
2043
+ if (base64String.startsWith("data:")) {
2044
+ const dataUriMatch = base64String.match(/^data:([^;]+);base64,/);
2045
+ if (dataUriMatch && dataUriMatch[1]) {
2046
+ mimeType = dataUriMatch[1];
2047
+ } else {
2048
+ mimeType = "application/octet-stream";
2049
+ }
2050
+ base64Data = base64String.split(",")[1] || "";
2051
+ } else {
2052
+ mimeType = getMimeTypeFromFileName(fileName) || "application/octet-stream";
2053
+ base64Data = base64String;
2054
+ }
2055
+ try {
2056
+ const byteCharacters = atob(base64Data);
2057
+ const byteNumbers = new Array(byteCharacters.length);
2058
+ for (let i = 0; i < byteCharacters.length; i++) {
2059
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
2060
+ }
2061
+ const byteArray = new Uint8Array(byteNumbers);
2062
+ const blob = new Blob([byteArray], { type: mimeType });
2063
+ const url = window.URL.createObjectURL(blob);
2064
+ const link2 = document.createElement("a");
2065
+ link2.href = url;
2066
+ link2.download = fileName;
2067
+ document.body.appendChild(link2);
2068
+ link2.click();
2069
+ document.body.removeChild(link2);
2070
+ window.URL.revokeObjectURL(url);
2071
+ } catch (error2) {
2072
+ console.error("Ошибка при обработке Base64:", error2);
2073
+ }
2074
+ };
2075
+ const getMimeTypeFromFileName = (fileName) => {
2076
+ var _a;
2077
+ const extension = (_a = fileName.split(".").pop()) == null ? void 0 : _a.toLowerCase();
2078
+ if (!extension) return void 0;
2079
+ const mimeTypes = {
2080
+ txt: "text/plain",
2081
+ pdf: "application/pdf",
2082
+ png: "image/png",
2083
+ jpg: "image/jpeg",
2084
+ jpeg: "image/jpeg",
2085
+ gif: "image/gif",
2086
+ svg: "image/svg+xml",
2087
+ doc: "application/msword",
2088
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
2089
+ xls: "application/vnd.ms-excel",
2090
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
2091
+ ppt: "application/vnd.ms-powerpoint",
2092
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
2093
+ json: "application/json",
2094
+ zip: "application/zip",
2095
+ mp3: "audio/mpeg",
2096
+ mp4: "video/mp4"
2097
+ };
2098
+ return mimeTypes[extension];
2099
+ };
2100
+ const preview$1 = "_preview_irgtv_1";
2101
+ const imageButton = "_imageButton_irgtv_7";
2102
+ const download = "_download_irgtv_27";
2024
2103
  const styles$n = {
2025
2104
  preview: preview$1,
2026
2105
  imageButton,
2027
2106
  download
2028
2107
  };
2029
- const FImagePreview = ({ src, alt = "", id, className, st }) => {
2108
+ const getExtensionFromMimeType = (mimeType) => {
2109
+ const cleanType = mimeType.split(";")[0].trim().toLowerCase();
2110
+ switch (cleanType) {
2111
+ case "image/jpeg":
2112
+ return "jpg";
2113
+ case "image/png":
2114
+ return "png";
2115
+ case "image/gif":
2116
+ return "gif";
2117
+ case "image/webp":
2118
+ return "webp";
2119
+ case "image/svg+xml":
2120
+ return "svg";
2121
+ case "image/avif":
2122
+ return "avif";
2123
+ case "image/bmp":
2124
+ return "bmp";
2125
+ case "image/x-icon":
2126
+ case "image/vnd.microsoft.icon":
2127
+ return "ico";
2128
+ default:
2129
+ return "";
2130
+ }
2131
+ };
2132
+ const getFileName = (url, fallback) => {
2133
+ if (url.startsWith("data:")) {
2134
+ const mimeMatch = url.match(/^data:([^;]+);/);
2135
+ const ext = mimeMatch ? getExtensionFromMimeType(mimeMatch[1]) : "png";
2136
+ const base = (fallback == null ? void 0 : fallback.trim()) || "image";
2137
+ return base.includes(".") ? base : `${base}.${ext || "png"}`;
2138
+ }
2139
+ try {
2140
+ const parsed = new URL(url, window.location.href);
2141
+ const segments = parsed.pathname.split("/").filter(Boolean);
2142
+ const lastSegment = segments.pop();
2143
+ if (lastSegment) {
2144
+ return decodeURIComponent(lastSegment);
2145
+ }
2146
+ } catch {
2147
+ const clean = url.split("?")[0].split("#")[0];
2148
+ const segments = clean.split("/").filter(Boolean);
2149
+ const lastSegment = segments.pop();
2150
+ if (lastSegment) {
2151
+ return decodeURIComponent(lastSegment);
2152
+ }
2153
+ }
2154
+ return (fallback == null ? void 0 : fallback.trim()) || "image";
2155
+ };
2156
+ const FImagePreview = ({ src, alt = "", id, className, st, fileName }) => {
2030
2157
  const [isOpen, setIsOpen] = useState(false);
2158
+ const handleDownload = async (event) => {
2159
+ event.stopPropagation();
2160
+ if (!src) return;
2161
+ const defaultFileName = getFileName(src, alt);
2162
+ const resolvedFileName = fileName || defaultFileName;
2163
+ try {
2164
+ if (src.startsWith("data:")) {
2165
+ fDownloadFileFromBase64(src, resolvedFileName);
2166
+ return;
2167
+ }
2168
+ const response = await fetch(src);
2169
+ if (!response.ok) {
2170
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
2171
+ }
2172
+ const blob = await response.blob();
2173
+ let finalName = resolvedFileName;
2174
+ if (!finalName.includes(".") && blob.type) {
2175
+ const ext = getExtensionFromMimeType(blob.type);
2176
+ if (ext) {
2177
+ finalName = `${finalName}.${ext}`;
2178
+ }
2179
+ }
2180
+ fDownloadBlobFile(blob, finalName);
2181
+ } catch {
2182
+ const link2 = document.createElement("a");
2183
+ link2.href = src;
2184
+ link2.download = resolvedFileName;
2185
+ link2.target = "_blank";
2186
+ link2.rel = "noreferrer noopener";
2187
+ document.body.appendChild(link2);
2188
+ link2.click();
2189
+ document.body.removeChild(link2);
2190
+ }
2191
+ };
2031
2192
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2032
2193
  /* @__PURE__ */ jsxs(
2033
2194
  "div",
@@ -2046,7 +2207,17 @@ const FImagePreview = ({ src, alt = "", id, className, st }) => {
2046
2207
  children: /* @__PURE__ */ jsx("img", { src, alt })
2047
2208
  }
2048
2209
  ),
2049
- /* @__PURE__ */ jsx("a", { className: styles$n.download, href: src, download: true, "aria-label": "Скачать изображение", title: "Скачать изображение", children: /* @__PURE__ */ jsx(FDownloadIcon, { color: "light", size: 18 }) })
2210
+ /* @__PURE__ */ jsx(
2211
+ "button",
2212
+ {
2213
+ type: "button",
2214
+ className: styles$n.download,
2215
+ onClick: handleDownload,
2216
+ "aria-label": "Скачать изображение",
2217
+ title: "Скачать изображение",
2218
+ children: /* @__PURE__ */ jsx(FDownloadIcon, { color: "light", size: 18 })
2219
+ }
2220
+ )
2050
2221
  ]
2051
2222
  }
2052
2223
  ),
@@ -8854,85 +9025,6 @@ const fConvertFileToBase64 = (file, deletePrefix = false) => {
8854
9025
  reader.readAsDataURL(file);
8855
9026
  });
8856
9027
  };
8857
- const fDownloadFileFromBase64 = (base64String, fileName) => {
8858
- if (!base64String) {
8859
- console.error("Invalid Base64 string");
8860
- return;
8861
- }
8862
- let mimeType;
8863
- let base64Data;
8864
- if (base64String.startsWith("data:")) {
8865
- const dataUriMatch = base64String.match(/^data:([^;]+);base64,/);
8866
- if (dataUriMatch && dataUriMatch[1]) {
8867
- mimeType = dataUriMatch[1];
8868
- } else {
8869
- mimeType = "application/octet-stream";
8870
- }
8871
- base64Data = base64String.split(",")[1] || "";
8872
- } else {
8873
- mimeType = getMimeTypeFromFileName(fileName) || "application/octet-stream";
8874
- base64Data = base64String;
8875
- }
8876
- try {
8877
- const byteCharacters = atob(base64Data);
8878
- const byteNumbers = new Array(byteCharacters.length);
8879
- for (let i = 0; i < byteCharacters.length; i++) {
8880
- byteNumbers[i] = byteCharacters.charCodeAt(i);
8881
- }
8882
- const byteArray = new Uint8Array(byteNumbers);
8883
- const blob = new Blob([byteArray], { type: mimeType });
8884
- const url = window.URL.createObjectURL(blob);
8885
- const link2 = document.createElement("a");
8886
- link2.href = url;
8887
- link2.download = fileName;
8888
- document.body.appendChild(link2);
8889
- link2.click();
8890
- document.body.removeChild(link2);
8891
- window.URL.revokeObjectURL(url);
8892
- } catch (error2) {
8893
- console.error("Ошибка при обработке Base64:", error2);
8894
- }
8895
- };
8896
- const getMimeTypeFromFileName = (fileName) => {
8897
- var _a;
8898
- const extension = (_a = fileName.split(".").pop()) == null ? void 0 : _a.toLowerCase();
8899
- if (!extension) return void 0;
8900
- const mimeTypes = {
8901
- txt: "text/plain",
8902
- pdf: "application/pdf",
8903
- png: "image/png",
8904
- jpg: "image/jpeg",
8905
- jpeg: "image/jpeg",
8906
- gif: "image/gif",
8907
- svg: "image/svg+xml",
8908
- doc: "application/msword",
8909
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
8910
- xls: "application/vnd.ms-excel",
8911
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
8912
- ppt: "application/vnd.ms-powerpoint",
8913
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
8914
- json: "application/json",
8915
- zip: "application/zip",
8916
- mp3: "audio/mpeg",
8917
- mp4: "video/mp4"
8918
- };
8919
- return mimeTypes[extension];
8920
- };
8921
- function fDownloadBlobFile(blob, fileName) {
8922
- if (!blob) {
8923
- throw new Error("Blob or string content must not be empty");
8924
- }
8925
- const blobObject = typeof blob === "string" ? new Blob([blob], { type: "text/plain;charset=utf-8" }) : blob;
8926
- const link2 = document.createElement("a");
8927
- const url = window.URL.createObjectURL(blobObject);
8928
- link2.href = url;
8929
- link2.download = fileName;
8930
- link2.style.display = "none";
8931
- document.body.appendChild(link2);
8932
- link2.click();
8933
- document.body.removeChild(link2);
8934
- window.URL.revokeObjectURL(url);
8935
- }
8936
9028
  function fBankRound(value, decimals = 2) {
8937
9029
  const str = String(value).trim();
8938
9030
  if (!/^[-+]?\d+(\.\d+)?$/.test(str)) return NaN;
@@ -9054,7 +9146,9 @@ const ERROR_FIELDS = [
9054
9146
  "detail",
9055
9147
  "traceId",
9056
9148
  "requestId",
9057
- "timestamp"
9149
+ "timestamp",
9150
+ "exception",
9151
+ "path"
9058
9152
  ];
9059
9153
  function isScalar(value) {
9060
9154
  return typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
@@ -9074,7 +9168,7 @@ function normalizeRequestErrorResponse(data) {
9074
9168
  for (const field of ERROR_FIELDS) {
9075
9169
  const value = data[field];
9076
9170
  if (!isScalar(value)) continue;
9077
- const text = String(value);
9171
+ const text = field === "path" ? normalizeErrorPath(String(value)) : String(value);
9078
9172
  result[field] = text.slice(
9079
9173
  0,
9080
9174
  field === "message" || field === "detail" ? LONG_ERROR_FIELD_LIMIT : SHORT_ERROR_FIELD_LIMIT
@@ -9098,6 +9192,53 @@ function getErrorResponseData(error2) {
9098
9192
  function getRequestErrorResponse(error2) {
9099
9193
  return normalizeRequestErrorResponse(getErrorResponseData(error2));
9100
9194
  }
9195
+ function normalizeErrorPath(value) {
9196
+ return new URL(value, "http://observer.invalid").pathname.replace(
9197
+ /\/(?:\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{16,})(?=\/|$)/gi,
9198
+ "/<id>"
9199
+ );
9200
+ }
9201
+ const SENSITIVE_BODY_FIELD = /password|passwd|pwd|token|auth|cookie|secret|session|csrf|email|e-mail|phone|mobile|telephone|address|passport|snils|inn|kpp|account|card|birth|fio|name|surname|patronymic|executor|waterUser|org|contract|docNum|basis|notes|comment|description|text|content|file|scan|парол|токен|куки|почт|телефон|адрес|паспорт|снилс|инн|фио|фамили|отчеств|^имя$/i;
9202
+ function scrubRequestText(value, limit) {
9203
+ return value.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => normalizeErrorPath(url)).replace(/\?[^\s"'<>)]*/g, "?").replace(/#[^\s"'<>)]*/g, "#").slice(0, limit);
9204
+ }
9205
+ function getRequestErrorBody(observer, method, outcome, error2, requestData) {
9206
+ try {
9207
+ if (!(observer == null ? void 0 : observer.captureErrorRequestBody) || outcome !== "failure" || !["POST", "PATCH"].includes(method.trim().toUpperCase())) return void 0;
9208
+ const candidate = error2 && typeof error2 === "object" ? error2 : void 0;
9209
+ let input = candidate && "requestBody" in candidate ? candidate.requestBody : (candidate == null ? void 0 : candidate.config) ? candidate.config.data : requestData;
9210
+ if (input === void 0) return void 0;
9211
+ if (typeof input === "string") {
9212
+ if (input === "[truncated]" || input === "[omitted]") return input;
9213
+ if (input.length > 65536) return "[truncated]";
9214
+ input = JSON.parse(input);
9215
+ if (typeof input === "string") return void 0;
9216
+ }
9217
+ if (input !== null && typeof input === "object" && !Array.isArray(input) && Object.getPrototypeOf(input) !== Object.prototype && Object.getPrototypeOf(input) !== null) return void 0;
9218
+ const visited = /* @__PURE__ */ new WeakSet();
9219
+ let remaining = 200;
9220
+ const sanitize = (value, depth) => {
9221
+ if (--remaining < 0 || depth > 6) return "[truncated]";
9222
+ if (value === null || typeof value === "boolean") return value;
9223
+ if (typeof value === "number") return Number.isFinite(value) ? value : "[omitted]";
9224
+ if (typeof value === "string") return scrubRequestText(value, 512);
9225
+ if (!value || typeof value !== "object") return "[omitted]";
9226
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return "[omitted]";
9227
+ if (visited.has(value)) return "[circular]";
9228
+ visited.add(value);
9229
+ const result2 = Array.isArray(value) ? value.slice(0, 50).map((item) => sanitize(item, depth + 1)) : Object.fromEntries(Object.keys(value).slice(0, 50).map((key) => [
9230
+ scrubRequestText(key, 128),
9231
+ SENSITIVE_BODY_FIELD.test(key) ? "[redacted]" : sanitize(value[key], depth + 1)
9232
+ ]));
9233
+ visited.delete(value);
9234
+ return result2;
9235
+ };
9236
+ const result = sanitize(input, 0);
9237
+ return new TextEncoder().encode(JSON.stringify(result)).byteLength <= 8192 ? result : "[truncated]";
9238
+ } catch {
9239
+ return void 0;
9240
+ }
9241
+ }
9101
9242
  const getRequestNow = () => {
9102
9243
  if (typeof performance !== "undefined" && typeof performance.now === "function") {
9103
9244
  return performance.now();
@@ -9157,7 +9298,7 @@ function notifyRequestObserver(observer, observation) {
9157
9298
  } catch {
9158
9299
  }
9159
9300
  }
9160
- async function observeRequest(observer, requestFn, method, url) {
9301
+ async function observeRequest(observer, requestFn, method, url, requestData) {
9161
9302
  if (!observer) {
9162
9303
  return requestFn();
9163
9304
  }
@@ -9179,6 +9320,7 @@ async function observeRequest(observer, requestFn, method, url) {
9179
9320
  const durationMs = calcDurationMs(startTime);
9180
9321
  const { outcome, errorCategory, status } = classifyRequestError(error2);
9181
9322
  const errorResponse = outcome === "failure" ? getRequestErrorResponse(error2) : void 0;
9323
+ const errorRequestBody = getRequestErrorBody(observer, normalizedMethod, outcome, error2, requestData);
9182
9324
  notifyRequestObserver(observer, {
9183
9325
  method: normalizedMethod,
9184
9326
  url,
@@ -9186,7 +9328,8 @@ async function observeRequest(observer, requestFn, method, url) {
9186
9328
  durationMs,
9187
9329
  outcome,
9188
9330
  errorCategory,
9189
- ...errorResponse ? { errorResponse } : {}
9331
+ ...errorResponse ? { errorResponse } : {},
9332
+ ...errorRequestBody !== void 0 ? { errorRequestBody } : {}
9190
9333
  });
9191
9334
  throw error2;
9192
9335
  }
@@ -9212,7 +9355,8 @@ const FLibraryProvider = ({
9212
9355
  effectiveObserver,
9213
9356
  () => config.apiService.request(url, reqConfig),
9214
9357
  (reqConfig == null ? void 0 : reqConfig.method) ?? "GET",
9215
- url
9358
+ url,
9359
+ reqConfig == null ? void 0 : reqConfig.data
9216
9360
  );
9217
9361
  }
9218
9362
  }
@@ -9280,6 +9424,7 @@ const useFApi = ({
9280
9424
  if (effectiveObserver) {
9281
9425
  const { outcome, errorCategory, status } = classifyRequestError(error22);
9282
9426
  const errorResponse = outcome === "failure" ? getRequestErrorResponse(error22) : void 0;
9427
+ const errorRequestBody = getRequestErrorBody(effectiveObserver, method, outcome, error22, config.data);
9283
9428
  notifyRequestObserver(effectiveObserver, {
9284
9429
  method,
9285
9430
  url,
@@ -9287,7 +9432,8 @@ const useFApi = ({
9287
9432
  durationMs: calcDurationMs(startTime),
9288
9433
  outcome,
9289
9434
  errorCategory,
9290
- ...errorResponse ? { errorResponse } : {}
9435
+ ...errorResponse ? { errorResponse } : {},
9436
+ ...errorRequestBody !== void 0 ? { errorRequestBody } : {}
9291
9437
  });
9292
9438
  }
9293
9439
  const err = error22;