fui-material 2.8.18 → 2.8.20

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.
@@ -1825,22 +1825,101 @@ const FDownloadIcon = React.forwardRef(({
1825
1825
  );
1826
1826
  });
1827
1827
  FDownloadIcon.displayName = "FDownloadIcon";
1828
- const overlay$1 = "_overlay_19vq5_1";
1829
- const entering$1 = "_entering_19vq5_15";
1830
- const exiting$1 = "_exiting_19vq5_19";
1831
- const viewport = "_viewport_19vq5_24";
1832
- const image = "_image_19vq5_33";
1833
- const zoomed = "_zoomed_19vq5_44";
1834
- const close$1 = "_close_19vq5_52";
1835
- const download$1 = "_download_19vq5_53";
1836
- const arrow = "_arrow_19vq5_54";
1837
- const left = "_left_19vq5_107";
1838
- const right = "_right_19vq5_111";
1839
- const indicators = "_indicators_19vq5_115";
1840
- const dot = "_dot_19vq5_124";
1841
- const active$4 = "_active_19vq5_133";
1842
- const spinner = "_spinner_19vq5_138";
1843
- const error$2 = "_error_19vq5_148";
1828
+ function fDownloadBlobFile(blob, fileName) {
1829
+ if (!blob) {
1830
+ throw new Error("Blob or string content must not be empty");
1831
+ }
1832
+ const blobObject = typeof blob === "string" ? new Blob([blob], { type: "text/plain;charset=utf-8" }) : blob;
1833
+ const link2 = document.createElement("a");
1834
+ const url = window.URL.createObjectURL(blobObject);
1835
+ link2.href = url;
1836
+ link2.download = fileName;
1837
+ link2.style.display = "none";
1838
+ document.body.appendChild(link2);
1839
+ link2.click();
1840
+ document.body.removeChild(link2);
1841
+ window.URL.revokeObjectURL(url);
1842
+ }
1843
+ const fDownloadFileFromBase64 = (base64String, fileName) => {
1844
+ if (!base64String) {
1845
+ console.error("Invalid Base64 string");
1846
+ return;
1847
+ }
1848
+ let mimeType;
1849
+ let base64Data;
1850
+ if (base64String.startsWith("data:")) {
1851
+ const dataUriMatch = base64String.match(/^data:([^;]+);base64,/);
1852
+ if (dataUriMatch && dataUriMatch[1]) {
1853
+ mimeType = dataUriMatch[1];
1854
+ } else {
1855
+ mimeType = "application/octet-stream";
1856
+ }
1857
+ base64Data = base64String.split(",")[1] || "";
1858
+ } else {
1859
+ mimeType = getMimeTypeFromFileName(fileName) || "application/octet-stream";
1860
+ base64Data = base64String;
1861
+ }
1862
+ try {
1863
+ const byteCharacters = atob(base64Data);
1864
+ const byteNumbers = new Array(byteCharacters.length);
1865
+ for (let i = 0; i < byteCharacters.length; i++) {
1866
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
1867
+ }
1868
+ const byteArray = new Uint8Array(byteNumbers);
1869
+ const blob = new Blob([byteArray], { type: mimeType });
1870
+ const url = window.URL.createObjectURL(blob);
1871
+ const link2 = document.createElement("a");
1872
+ link2.href = url;
1873
+ link2.download = fileName;
1874
+ document.body.appendChild(link2);
1875
+ link2.click();
1876
+ document.body.removeChild(link2);
1877
+ window.URL.revokeObjectURL(url);
1878
+ } catch (error2) {
1879
+ console.error("Ошибка при обработке Base64:", error2);
1880
+ }
1881
+ };
1882
+ const getMimeTypeFromFileName = (fileName) => {
1883
+ var _a;
1884
+ const extension = (_a = fileName.split(".").pop()) == null ? void 0 : _a.toLowerCase();
1885
+ if (!extension) return void 0;
1886
+ const mimeTypes = {
1887
+ txt: "text/plain",
1888
+ pdf: "application/pdf",
1889
+ png: "image/png",
1890
+ jpg: "image/jpeg",
1891
+ jpeg: "image/jpeg",
1892
+ gif: "image/gif",
1893
+ svg: "image/svg+xml",
1894
+ doc: "application/msword",
1895
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1896
+ xls: "application/vnd.ms-excel",
1897
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1898
+ ppt: "application/vnd.ms-powerpoint",
1899
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1900
+ json: "application/json",
1901
+ zip: "application/zip",
1902
+ mp3: "audio/mpeg",
1903
+ mp4: "video/mp4"
1904
+ };
1905
+ return mimeTypes[extension];
1906
+ };
1907
+ const overlay$1 = "_overlay_1x5hi_1";
1908
+ const entering$1 = "_entering_1x5hi_15";
1909
+ const exiting$1 = "_exiting_1x5hi_19";
1910
+ const viewport = "_viewport_1x5hi_24";
1911
+ const image = "_image_1x5hi_33";
1912
+ const zoomed = "_zoomed_1x5hi_44";
1913
+ const close$1 = "_close_1x5hi_52";
1914
+ const download$1 = "_download_1x5hi_53";
1915
+ const arrow = "_arrow_1x5hi_54";
1916
+ const left = "_left_1x5hi_108";
1917
+ const right = "_right_1x5hi_112";
1918
+ const indicators = "_indicators_1x5hi_116";
1919
+ const dot = "_dot_1x5hi_125";
1920
+ const active$4 = "_active_1x5hi_134";
1921
+ const spinner = "_spinner_1x5hi_139";
1922
+ const error$2 = "_error_1x5hi_149";
1844
1923
  const styles$o = {
1845
1924
  overlay: overlay$1,
1846
1925
  entering: entering$1,
@@ -1860,6 +1939,54 @@ const styles$o = {
1860
1939
  error: error$2
1861
1940
  };
1862
1941
  let openImageViewersCount = 0;
1942
+ const getExtensionFromMimeType$1 = (mimeType) => {
1943
+ const cleanType = mimeType.split(";")[0].trim().toLowerCase();
1944
+ switch (cleanType) {
1945
+ case "image/jpeg":
1946
+ return "jpg";
1947
+ case "image/png":
1948
+ return "png";
1949
+ case "image/gif":
1950
+ return "gif";
1951
+ case "image/webp":
1952
+ return "webp";
1953
+ case "image/svg+xml":
1954
+ return "svg";
1955
+ case "image/avif":
1956
+ return "avif";
1957
+ case "image/bmp":
1958
+ return "bmp";
1959
+ case "image/x-icon":
1960
+ case "image/vnd.microsoft.icon":
1961
+ return "ico";
1962
+ default:
1963
+ return "";
1964
+ }
1965
+ };
1966
+ const getFileName$1 = (url, fallback) => {
1967
+ if (url.startsWith("data:")) {
1968
+ const mimeMatch = url.match(/^data:([^;]+);/);
1969
+ const ext = mimeMatch ? getExtensionFromMimeType$1(mimeMatch[1]) : "png";
1970
+ const base = "image";
1971
+ return base.includes(".") ? base : `${base}.${ext || "png"}`;
1972
+ }
1973
+ try {
1974
+ const parsed = new URL(url, window.location.href);
1975
+ const segments = parsed.pathname.split("/").filter(Boolean);
1976
+ const lastSegment = segments.pop();
1977
+ if (lastSegment) {
1978
+ return decodeURIComponent(lastSegment);
1979
+ }
1980
+ } catch {
1981
+ const clean = url.split("?")[0].split("#")[0];
1982
+ const segments = clean.split("/").filter(Boolean);
1983
+ const lastSegment = segments.pop();
1984
+ if (lastSegment) {
1985
+ return decodeURIComponent(lastSegment);
1986
+ }
1987
+ }
1988
+ return "image";
1989
+ };
1863
1990
  const FOpenImgFull = ({
1864
1991
  id,
1865
1992
  className,
@@ -1868,7 +1995,9 @@ const FOpenImgFull = ({
1868
1995
  selectedImg,
1869
1996
  setSelectedImg,
1870
1997
  handleClose,
1871
- openAndClose
1998
+ openAndClose,
1999
+ fileName,
2000
+ fileNames
1872
2001
  }) => {
1873
2002
  const [isMounted, setIsMounted] = useState(openAndClose);
1874
2003
  const [isClosing, setIsClosing] = useState(false);
@@ -1925,6 +2054,40 @@ const FOpenImgFull = ({
1925
2054
  const nextIndex = selectedImg + offset;
1926
2055
  if (nextIndex >= 0 && nextIndex < imagesUrl.length) setSelectedImg(nextIndex);
1927
2056
  };
2057
+ const handleDownload = async (event) => {
2058
+ event.stopPropagation();
2059
+ if (!imageUrl) return;
2060
+ const defaultFileName = getFileName$1(imageUrl);
2061
+ const resolvedFileName = (fileNames == null ? void 0 : fileNames[selectedImg]) || fileName || defaultFileName;
2062
+ try {
2063
+ if (imageUrl.startsWith("data:")) {
2064
+ fDownloadFileFromBase64(imageUrl, resolvedFileName);
2065
+ return;
2066
+ }
2067
+ const response = await fetch(imageUrl);
2068
+ if (!response.ok) {
2069
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
2070
+ }
2071
+ const blob = await response.blob();
2072
+ let finalName = resolvedFileName;
2073
+ if (!finalName.includes(".") && blob.type) {
2074
+ const ext = getExtensionFromMimeType$1(blob.type);
2075
+ if (ext) {
2076
+ finalName = `${finalName}.${ext}`;
2077
+ }
2078
+ }
2079
+ fDownloadBlobFile(blob, finalName);
2080
+ } catch {
2081
+ const link2 = document.createElement("a");
2082
+ link2.href = imageUrl;
2083
+ link2.download = resolvedFileName;
2084
+ link2.target = "_blank";
2085
+ link2.rel = "noreferrer noopener";
2086
+ document.body.appendChild(link2);
2087
+ link2.click();
2088
+ document.body.removeChild(link2);
2089
+ }
2090
+ };
1928
2091
  return createPortal(
1929
2092
  /* @__PURE__ */ jsxs(
1930
2093
  "div",
@@ -1937,7 +2100,17 @@ const FOpenImgFull = ({
1937
2100
  },
1938
2101
  children: [
1939
2102
  handleClose && /* @__PURE__ */ jsx("button", { type: "button", className: styles$o.close, onClick: handleClose, "aria-label": "Закрыть просмотр изображений", children: /* @__PURE__ */ jsx(FCloseIcon, { color: "light", size: 24 }) }),
1940
- /* @__PURE__ */ jsx("a", { className: styles$o.download, href: imageUrl, download: true, "aria-label": "Скачать изображение", title: "Скачать изображение", children: /* @__PURE__ */ jsx(FDownloadIcon, { color: "light", size: 20 }) }),
2103
+ /* @__PURE__ */ jsx(
2104
+ "button",
2105
+ {
2106
+ type: "button",
2107
+ className: styles$o.download,
2108
+ onClick: handleDownload,
2109
+ "aria-label": "Скачать изображение",
2110
+ title: "Скачать изображение",
2111
+ children: /* @__PURE__ */ jsx(FDownloadIcon, { color: "light", size: 20 })
2112
+ }
2113
+ ),
1941
2114
  selectedImg > 0 && /* @__PURE__ */ jsx("button", { type: "button", className: `${styles$o.arrow} ${styles$o.left}`, onClick: () => changeImage(-1), "aria-label": "Предыдущее изображение", children: /* @__PURE__ */ jsx(FArrowIcon, { direction: "left", color: "light", size: 28 }) }),
1942
2115
  /* @__PURE__ */ jsxs("div", { className: styles$o.viewport, children: [
1943
2116
  isLoading && !hasError && /* @__PURE__ */ jsx("span", { className: styles$o.spinner, "aria-label": "Загрузка изображения" }),
@@ -2018,85 +2191,6 @@ const FOpenImgFull = ({
2018
2191
  document.body
2019
2192
  );
2020
2193
  };
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
2194
  const preview$1 = "_preview_irgtv_1";
2101
2195
  const imageButton = "_imageButton_irgtv_7";
2102
2196
  const download = "_download_irgtv_27";
@@ -2228,7 +2322,8 @@ const FImagePreview = ({ src, alt = "", id, className, st, fileName }) => {
2228
2322
  selectedImg: 0,
2229
2323
  setSelectedImg: () => void 0,
2230
2324
  openAndClose: isOpen,
2231
- handleClose: () => setIsOpen(false)
2325
+ handleClose: () => setIsOpen(false),
2326
+ fileName
2232
2327
  }
2233
2328
  )
2234
2329
  ] });
@@ -4830,6 +4925,92 @@ const FStatusBadge = ({
4830
4925
  /* @__PURE__ */ jsx("span", { children })
4831
4926
  ] });
4832
4927
  };
4928
+ function FProgressiveList({
4929
+ items,
4930
+ chunkSize = 6,
4931
+ estimatedItemHeight = 280,
4932
+ keyExtractor,
4933
+ className,
4934
+ st,
4935
+ showProgress = true,
4936
+ renderProgress,
4937
+ children,
4938
+ onLoadMore,
4939
+ hasMore = false,
4940
+ isLoadingMore = false,
4941
+ loader: loader2,
4942
+ threshold = 250
4943
+ }) {
4944
+ const totalCount = items.length;
4945
+ const [renderedCount, setRenderedCount] = useState(() => Math.min(chunkSize, totalCount));
4946
+ const sentinelRef = useRef(null);
4947
+ const prevFirstKeyRef = useRef(void 0);
4948
+ const prevTotalCountRef = useRef(totalCount);
4949
+ useEffect(() => {
4950
+ const currentFirstKey = items.length > 0 ? keyExtractor(items[0], 0) : void 0;
4951
+ const isReset = currentFirstKey !== prevFirstKeyRef.current || totalCount < prevTotalCountRef.current;
4952
+ if (isReset) {
4953
+ setRenderedCount(Math.min(chunkSize, totalCount));
4954
+ }
4955
+ prevFirstKeyRef.current = currentFirstKey;
4956
+ prevTotalCountRef.current = totalCount;
4957
+ }, [items, keyExtractor, chunkSize, totalCount]);
4958
+ useEffect(() => {
4959
+ if (renderedCount >= totalCount) return;
4960
+ const frameId = requestAnimationFrame(() => {
4961
+ setRenderedCount((prev) => Math.min(prev + chunkSize, totalCount));
4962
+ });
4963
+ return () => cancelAnimationFrame(frameId);
4964
+ }, [renderedCount, totalCount, chunkSize]);
4965
+ useEffect(() => {
4966
+ if (!onLoadMore || !hasMore || isLoadingMore) return;
4967
+ if (renderedCount < totalCount) return;
4968
+ const sentinelEl = sentinelRef.current;
4969
+ if (!sentinelEl) return;
4970
+ const observer = new IntersectionObserver(
4971
+ (entries) => {
4972
+ const [entry] = entries;
4973
+ if (entry && entry.isIntersecting) {
4974
+ onLoadMore();
4975
+ }
4976
+ },
4977
+ {
4978
+ rootMargin: `0px 0px ${threshold}px 0px`,
4979
+ threshold: 0
4980
+ }
4981
+ );
4982
+ observer.observe(sentinelEl);
4983
+ return () => {
4984
+ observer.disconnect();
4985
+ };
4986
+ }, [onLoadMore, hasMore, isLoadingMore, renderedCount, totalCount, threshold]);
4987
+ const visibleItems = items.slice(0, renderedCount);
4988
+ const itemWrapperStyle = {
4989
+ contentVisibility: "auto",
4990
+ containIntrinsicSize: `0 ${estimatedItemHeight}px`,
4991
+ marginBottom: 16
4992
+ };
4993
+ return /* @__PURE__ */ jsxs("div", { className, style: st, children: [
4994
+ visibleItems.map((item, index) => /* @__PURE__ */ jsx(
4995
+ "div",
4996
+ {
4997
+ style: itemWrapperStyle,
4998
+ children: children(item, index)
4999
+ },
5000
+ keyExtractor(item, index)
5001
+ )),
5002
+ showProgress && renderProgress && renderedCount < totalCount && /* @__PURE__ */ jsx("div", { style: { textAlign: "center", padding: "8px 0" }, children: renderProgress(renderedCount, totalCount) }),
5003
+ onLoadMore && hasMore && /* @__PURE__ */ jsx(
5004
+ "div",
5005
+ {
5006
+ ref: sentinelRef,
5007
+ style: { height: 1, width: "100%", pointerEvents: "none", visibility: "hidden" },
5008
+ "aria-hidden": "true"
5009
+ }
5010
+ ),
5011
+ isLoadingMore && /* @__PURE__ */ jsx("div", { style: { padding: "16px 0", textAlign: "center" }, children: loader2 ?? /* @__PURE__ */ jsx(FProgress, {}) })
5012
+ ] });
5013
+ }
4833
5014
  const FPlusIcon = React.forwardRef(({
4834
5015
  color = "primary",
4835
5016
  size = 30,
@@ -9655,9 +9836,16 @@ const useFApiQuery = (queryKey, url, config, options) => {
9655
9836
  function useFManualApiQuery(queryKey, url, initialParams, config, options) {
9656
9837
  const { apiService } = useFLibraryContext();
9657
9838
  const queryClient = useQueryClient();
9839
+ const abortControllerRef = useRef(null);
9658
9840
  const [params, setParams] = useState(initialParams);
9659
9841
  const [urlParams, setUrlParams] = useState(options == null ? void 0 : options.urlParams);
9660
9842
  const [dopUrl, setDopUrl] = useState(void 0);
9843
+ useEffect(() => {
9844
+ return () => {
9845
+ var _a;
9846
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
9847
+ };
9848
+ }, []);
9661
9849
  const {
9662
9850
  onErrorNotification = false,
9663
9851
  onErrorLog = false,
@@ -9694,6 +9882,9 @@ function useFManualApiQuery(queryKey, url, initialParams, config, options) {
9694
9882
  ...restOptions
9695
9883
  });
9696
9884
  const handleSideErrors = useCallback((error2) => {
9885
+ if (axios.isCancel(error2)) {
9886
+ return;
9887
+ }
9697
9888
  if (onErrorNotification) {
9698
9889
  fNotification({
9699
9890
  variant: "error",
@@ -9715,10 +9906,21 @@ function useFManualApiQuery(queryKey, url, initialParams, config, options) {
9715
9906
  newParams,
9716
9907
  newUrlParams,
9717
9908
  preserveParams = false,
9909
+ mergeParams = false,
9718
9910
  dopUrl: newDopUrl,
9719
9911
  preserveUrlParams = false
9720
9912
  } = {}) => {
9721
- const updatedParams = preserveParams && newParams === void 0 ? params : newParams;
9913
+ if (abortControllerRef.current) {
9914
+ abortControllerRef.current.abort();
9915
+ }
9916
+ abortControllerRef.current = new AbortController();
9917
+ const activeSignal = abortControllerRef.current.signal;
9918
+ let updatedParams;
9919
+ if (mergeParams && params && typeof params === "object" && newParams && typeof newParams === "object") {
9920
+ updatedParams = { ...params, ...newParams };
9921
+ } else {
9922
+ updatedParams = preserveParams && newParams === void 0 ? params : newParams;
9923
+ }
9722
9924
  const updatedUrlParams = newUrlParams === void 0 ? urlParams : preserveUrlParams ? { ...urlParams, ...newUrlParams } : newUrlParams;
9723
9925
  const updatedDopUrl = newDopUrl !== void 0 ? newDopUrl : dopUrl;
9724
9926
  setParams(updatedParams);
@@ -9730,7 +9932,20 @@ function useFManualApiQuery(queryKey, url, initialParams, config, options) {
9730
9932
  try {
9731
9933
  const rawResult = await queryClient.fetchQuery({
9732
9934
  queryKey: nextQueryKey,
9733
- queryFn: ({ signal }) => fetcher(updatedParams, updatedUrlParams, updatedDopUrl, signal),
9935
+ queryFn: ({ signal }) => {
9936
+ var _a;
9937
+ if (signal) {
9938
+ if (signal.aborted) {
9939
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
9940
+ } else {
9941
+ signal.addEventListener("abort", () => {
9942
+ var _a2;
9943
+ (_a2 = abortControllerRef.current) == null ? void 0 : _a2.abort();
9944
+ }, { once: true });
9945
+ }
9946
+ }
9947
+ return fetcher(updatedParams, updatedUrlParams, updatedDopUrl, activeSignal);
9948
+ },
9734
9949
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9735
9950
  staleTime: restOptions.staleTime
9736
9951
  });
@@ -9745,6 +9960,196 @@ function useFManualApiQuery(queryKey, url, initialParams, config, options) {
9745
9960
  }, [params, urlParams, dopUrl, queryKey, fetcher, queryClient, restOptions, handleSideErrors]);
9746
9961
  return { ...query, run, params, urlParams };
9747
9962
  }
9963
+ function extractDefaultData(raw) {
9964
+ if (!raw || typeof raw !== "object") {
9965
+ return raw;
9966
+ }
9967
+ const record = raw;
9968
+ const candidate = record.result ?? record.items ?? record.content ?? record.data;
9969
+ return candidate !== void 0 ? candidate : raw;
9970
+ }
9971
+ function extractDefaultPageMeta(raw, defaultSize, currentPage) {
9972
+ if (!raw || typeof raw !== "object") return void 0;
9973
+ const record = raw;
9974
+ const p = record.page ?? record.pagination;
9975
+ if (!p || typeof p !== "object") return void 0;
9976
+ const totalItems = Number(p.totalItems ?? p.totalElements ?? p.totalCount ?? p.total ?? 0);
9977
+ const size = Number(p.size ?? p.pageSize ?? p.limit ?? defaultSize);
9978
+ const totalPages = Number(p.totalPages ?? p.pageCount ?? (size > 0 ? Math.ceil(totalItems / size) : 1));
9979
+ const number = Number(p.number ?? p.currentPage ?? p.page ?? currentPage);
9980
+ return { totalItems, size, totalPages, number };
9981
+ }
9982
+ function useFManualPagination(options) {
9983
+ const {
9984
+ queryKey,
9985
+ url,
9986
+ defaultPageSize = 20,
9987
+ initialFilters,
9988
+ method = "POST",
9989
+ config,
9990
+ mode = "pagination",
9991
+ select,
9992
+ selectPage,
9993
+ appendData,
9994
+ onErrorNotification = true,
9995
+ onErrorLog = false,
9996
+ onLoadingLog = false,
9997
+ onSuccessNotification = false,
9998
+ notificationTitle,
9999
+ notificationBody,
10000
+ loadingNotificationTitle,
10001
+ loadingNotificationBody,
10002
+ successNotificationTitle,
10003
+ successNotificationBody
10004
+ } = options;
10005
+ const [page, setPage] = useState(1);
10006
+ const [pageSize, setPageSize] = useState(defaultPageSize);
10007
+ const [accumulatedData, setAccumulatedData] = useState(void 0);
10008
+ const [isFetchingMore, setIsFetchingMore] = useState(false);
10009
+ const isInfinite = mode === "infinite";
10010
+ useEffect(() => {
10011
+ setPageSize(defaultPageSize);
10012
+ }, [defaultPageSize]);
10013
+ const query = useFManualApiQuery(
10014
+ queryKey,
10015
+ url,
10016
+ initialFilters ? { ...initialFilters, page: 1, size: defaultPageSize } : void 0,
10017
+ config,
10018
+ {
10019
+ method,
10020
+ onErrorNotification,
10021
+ onErrorLog,
10022
+ onLoadingLog,
10023
+ onSuccessNotification,
10024
+ notificationTitle,
10025
+ notificationBody,
10026
+ loadingNotificationTitle,
10027
+ loadingNotificationBody,
10028
+ successNotificationTitle,
10029
+ successNotificationBody
10030
+ }
10031
+ );
10032
+ const rawData = query.data;
10033
+ const pageMeta = useMemo(() => {
10034
+ if (!rawData) return void 0;
10035
+ if (selectPage) return selectPage(rawData);
10036
+ return extractDefaultPageMeta(rawData, pageSize, page);
10037
+ }, [rawData, selectPage, pageSize, page]);
10038
+ const currentExtractedData = useMemo(() => {
10039
+ if (!rawData) return void 0;
10040
+ if (select) return select(rawData);
10041
+ return extractDefaultData(rawData);
10042
+ }, [rawData, select]);
10043
+ const meta = useMemo(() => {
10044
+ if (!rawData || typeof rawData !== "object") return void 0;
10045
+ return rawData.meta;
10046
+ }, [rawData]);
10047
+ const data = isInfinite ? accumulatedData : currentExtractedData;
10048
+ const extractResult = useCallback((rawResult) => {
10049
+ if (select) return select(rawResult);
10050
+ return extractDefaultData(rawResult);
10051
+ }, [select]);
10052
+ const defaultAppend = useCallback((prev, next) => {
10053
+ if (appendData) return appendData(prev, next);
10054
+ if (Array.isArray(next)) {
10055
+ const prevArray = Array.isArray(prev) ? prev : [];
10056
+ return [...prevArray, ...next];
10057
+ }
10058
+ return next;
10059
+ }, [appendData]);
10060
+ const submitFilters = useCallback(async ({
10061
+ payload,
10062
+ resetPage = true,
10063
+ dopUrl
10064
+ }) => {
10065
+ const targetPage = resetPage ? 1 : page;
10066
+ if (resetPage) {
10067
+ setPage(1);
10068
+ if (isInfinite) {
10069
+ setAccumulatedData(void 0);
10070
+ }
10071
+ }
10072
+ const rawResponse = await query.run({
10073
+ newParams: { ...payload, page: targetPage, size: pageSize },
10074
+ dopUrl
10075
+ });
10076
+ const extracted = extractResult(rawResponse);
10077
+ if (isInfinite) {
10078
+ setAccumulatedData(extracted);
10079
+ }
10080
+ return extracted;
10081
+ }, [query, page, pageSize, isInfinite, extractResult]);
10082
+ const changePage = useCallback(async (newPage) => {
10083
+ if (isInfinite) {
10084
+ setIsFetchingMore(true);
10085
+ try {
10086
+ const rawResponse = await query.run({
10087
+ newParams: { page: newPage, size: pageSize },
10088
+ mergeParams: true
10089
+ });
10090
+ const extracted = extractResult(rawResponse);
10091
+ setAccumulatedData((prev) => defaultAppend(prev, extracted));
10092
+ setPage(newPage);
10093
+ return extracted;
10094
+ } finally {
10095
+ setIsFetchingMore(false);
10096
+ }
10097
+ } else {
10098
+ setPage(newPage);
10099
+ const rawResponse = await query.run({
10100
+ newParams: { page: newPage, size: pageSize },
10101
+ mergeParams: true
10102
+ });
10103
+ return extractResult(rawResponse);
10104
+ }
10105
+ }, [query, pageSize, isInfinite, extractResult, defaultAppend]);
10106
+ const hasMore = useMemo(() => {
10107
+ if (!pageMeta) return false;
10108
+ return pageMeta.number < pageMeta.totalPages;
10109
+ }, [pageMeta]);
10110
+ const loadMore = useCallback(async () => {
10111
+ if (query.isFetching || isFetchingMore || !hasMore) {
10112
+ return void 0;
10113
+ }
10114
+ const nextPage = ((pageMeta == null ? void 0 : pageMeta.number) ?? page) + 1;
10115
+ return changePage(nextPage);
10116
+ }, [query.isFetching, isFetchingMore, hasMore, pageMeta, page, changePage]);
10117
+ const paginationProps = useMemo(() => ({
10118
+ currentPage: (pageMeta == null ? void 0 : pageMeta.number) ?? page,
10119
+ totalCount: (pageMeta == null ? void 0 : pageMeta.totalItems) ?? 0,
10120
+ pageSize: (pageMeta == null ? void 0 : pageMeta.size) ?? pageSize,
10121
+ siblingCount: 1,
10122
+ onPageChange: (newPage) => {
10123
+ if (typeof newPage === "number") {
10124
+ changePage(newPage).catch(() => {
10125
+ });
10126
+ } else if (newPage === "prev") {
10127
+ changePage(Math.max(1, ((pageMeta == null ? void 0 : pageMeta.number) ?? page) - 1)).catch(() => {
10128
+ });
10129
+ } else if (newPage === "next") {
10130
+ const max = (pageMeta == null ? void 0 : pageMeta.totalPages) ?? page;
10131
+ changePage(Math.min(max, ((pageMeta == null ? void 0 : pageMeta.number) ?? page) + 1)).catch(() => {
10132
+ });
10133
+ }
10134
+ }
10135
+ }), [pageMeta, page, pageSize, changePage]);
10136
+ return {
10137
+ data,
10138
+ result: data,
10139
+ rawResponse: rawData,
10140
+ meta,
10141
+ pageMeta,
10142
+ isLoading: query.isLoading,
10143
+ isFetching: query.isFetching,
10144
+ isFetchingMore,
10145
+ hasMore,
10146
+ error: query.error,
10147
+ submitFilters,
10148
+ changePage,
10149
+ loadMore,
10150
+ paginationProps
10151
+ };
10152
+ }
9748
10153
  const waterError = "_waterError_10t7p_1";
9749
10154
  const scene = "_scene_10t7p_12";
9750
10155
  const waves = "_waves_10t7p_18";
@@ -9890,6 +10295,7 @@ export {
9890
10295
  FPlusIcon,
9891
10296
  FPreloader,
9892
10297
  FProgress,
10298
+ FProgressiveList,
9893
10299
  FRadioButton,
9894
10300
  FReloadIcon,
9895
10301
  FSafeIcon,
@@ -9950,6 +10356,7 @@ export {
9950
10356
  useFApiQuery,
9951
10357
  useFLibraryContext,
9952
10358
  useFLibraryContextOptional,
9953
- useFManualApiQuery
10359
+ useFManualApiQuery,
10360
+ useFManualPagination
9954
10361
  };
9955
10362
  //# sourceMappingURL=f-ui-kit.es.js.map