@seplan/diti-ds
Version:
Reusable UI component library developed by DITI (Technology and Innovation Directorate of SEPLAN PI) based on Mantine and Tailwind CSS
434 lines (433 loc) • 12.7 kB
JavaScript
import { useMemo, useCallback, createContext, useContext, useState, useEffect } from "react";
import { jsx } from "react/jsx-runtime";
import { useQueryState, parseAsInteger, parseAsArrayOf, parseAsString, useQueryStates } from "nuqs";
import { notifications } from "@mantine/notifications";
import QRCode from "qrcode";
import { jsPDF } from "jspdf";
const useSortableColumn = (column, sortKey) => {
const sortConfig = useMemo(() => {
if (!sortKey) return { column: null, isDescending: false };
const isDescending2 = sortKey.startsWith("-");
const sortColumn = isDescending2 ? sortKey.slice(1) : sortKey;
return {
column: sortColumn,
isDescending: isDescending2
};
}, [sortKey]);
const isSorted = sortConfig.column === column;
const isDescending = isSorted && sortConfig.isDescending;
return { isSorted, isDescending };
};
const FiltersContext = createContext(
void 0
);
function FiltersProvider({
children,
groupKeys = []
}) {
const [page, setPage] = useQueryState("page", parseAsInteger.withDefault(1));
const queryStateConfig = useMemo(() => {
return groupKeys.reduce(
(acc, key) => {
acc[key] = parseAsArrayOf(parseAsString).withOptions({
shallow: false
});
return acc;
},
{}
);
}, [groupKeys]);
const [queryStates, setQueryStates] = useQueryStates(queryStateConfig);
const filterGroups = useMemo(() => {
return groupKeys.reduce((acc, key) => {
const value = queryStates[key];
acc[key] = new Set(Array.isArray(value) ? value : []);
return acc;
}, {});
}, [groupKeys, queryStates]);
const getGroupSelected = useCallback(
(groupKey) => {
return filterGroups[groupKey] || /* @__PURE__ */ new Set();
},
[filterGroups]
);
const toggleOption = useCallback(
(groupKey, value) => {
setQueryStates((prev) => {
const currentSelection = new Set(prev[groupKey] || []);
if (currentSelection.has(value)) {
currentSelection.delete(value);
} else {
currentSelection.add(value);
}
return { ...prev, [groupKey]: Array.from(currentSelection) };
});
setPage(1);
},
[setQueryStates, setPage]
);
const clearGroup = useCallback(
(groupKey) => {
setQueryStates({ [groupKey]: null });
setPage(1);
},
[setQueryStates, setPage]
);
const clearAll = useCallback(() => {
const clearedGroups = groupKeys.reduce(
(acc, key) => {
acc[key] = null;
return acc;
},
{}
);
setQueryStates({ ...clearedGroups });
setPage(1);
}, [groupKeys, setQueryStates, setPage]);
const getTotalSelectedCount = useCallback(() => {
return Object.values(filterGroups).reduce(
(total, group) => total + group.size,
0
);
}, [filterGroups]);
const getGroupSelectedCount = useCallback(
(groupKey) => {
return filterGroups[groupKey]?.size || 0;
},
[filterGroups]
);
const filters = useMemo(() => {
return groupKeys.reduce((acc, key) => {
const selected = Array.from(filterGroups[key] || /* @__PURE__ */ new Set());
acc[key] = selected.length > 0 ? selected : null;
return acc;
}, {});
}, [filterGroups, groupKeys]);
const setFilters = useCallback((newFilters) => {
const updates = Object.keys(newFilters).reduce((acc, key) => {
const value = newFilters[key];
acc[key] = Array.isArray(value) ? value : value ? [value] : null;
return acc;
}, {});
setQueryStates(updates);
setPage(1);
}, [setQueryStates, setPage]);
const contextValue = useMemo(
() => ({
filterGroups,
getGroupSelected,
toggleOption,
clearGroup,
clearAll,
getTotalSelectedCount,
getGroupSelectedCount,
page,
filters,
setFilters
}),
[
filterGroups,
getGroupSelected,
toggleOption,
clearGroup,
clearAll,
getTotalSelectedCount,
getGroupSelectedCount,
page,
filters,
setFilters
]
);
return /* @__PURE__ */ jsx(FiltersContext.Provider, { value: contextValue, children });
}
const useFilters = () => {
const context = useContext(FiltersContext);
if (!context) {
throw new Error("useFilters must be used within a FiltersProvider");
}
return context;
};
const SelectedItemsContext = createContext(void 0);
function useSelectedItems() {
const context = useContext(SelectedItemsContext);
if (!context) {
throw new Error(
"useSelectedItems must be used within SelectedItemsContext.Provider"
);
}
return context;
}
function SelectedItemsProvider({
children,
searchParamKey = "selected"
}) {
const [selectedItems, setSelectedItems] = useQueryState(
searchParamKey,
parseAsArrayOf(parseAsString).withDefault([]).withOptions({
shallow: false
})
);
const isSelected = useCallback(
(item) => {
return selectedItems.includes(item);
},
[selectedItems]
);
const toggle = useCallback(
(value) => {
if (isSelected(value)) {
setSelectedItems((prev) => prev.filter((item) => item !== value));
} else {
setSelectedItems((prev) => [...prev, value]);
}
},
[setSelectedItems, isSelected]
);
const clear = useCallback(() => {
setSelectedItems([]);
}, [setSelectedItems]);
const contextValue = useMemo(
() => ({
selectedItems: new Set(selectedItems),
toggle,
clear
}),
[selectedItems, toggle, clear]
);
return /* @__PURE__ */ jsx(SelectedItemsContext.Provider, { value: contextValue, children });
}
function useClipboard(timeout = 500) {
const [copied, setCopied] = useState(false);
async function copy(text) {
try {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), timeout);
return;
}
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
textArea.style.opacity = "0";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand("copy");
if (!successful) throw new Error("Copy command failed");
setCopied(true);
setTimeout(() => setCopied(false), timeout);
} finally {
document.body.removeChild(textArea);
}
} catch (err) {
console.error("Failed to copy:", err);
notifications.show({
message: "Não foi possível copiar o texto",
color: "red"
});
}
}
return { copied, copy };
}
function useDownloadBlob(options = {}) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const downloadBlob = async (url, filename, params) => {
try {
setIsLoading(true);
setError(null);
const urlObj = new URL(url, window.location.origin);
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== void 0 && value !== null) {
if (Array.isArray(value)) {
value.forEach((v) => urlObj.searchParams.append(key, String(v)));
} else {
urlObj.searchParams.append(key, String(value));
}
}
});
}
const response = await fetch(urlObj.toString(), {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
if (!response.ok) {
throw new Error(`Failed to download file: ${response.statusText}`);
}
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = downloadUrl;
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
options.onSuccess?.();
} catch (err) {
const error2 = err instanceof Error ? err : new Error("Download failed");
setError(error2);
options.onError?.(error2);
console.error("Error downloading file:", error2);
} finally {
setIsLoading(false);
}
};
return {
downloadBlob,
isLoading,
error
};
}
function useMediaQuery(query) {
const [matches, setMatches] = useState(false);
useEffect(() => {
const mediaQuery = window.matchMedia(query);
const handleChange = () => setMatches(mediaQuery.matches);
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, [query]);
return matches;
}
function usePaginationReset({
shallow = false
} = {}) {
const [page, setPage] = useQueryState(
"page",
parseAsInteger.withDefault(1).withOptions({
shallow
})
);
const resetPagination = useCallback(() => {
setPage(1);
}, [setPage]);
return {
page,
resetPagination
};
}
function useQRCodeGenerator() {
const [isGenerating, setIsGenerating] = useState(false);
const generateAndDownloadPDF = async ({
url,
backgroundImagePath,
orientation = "portrait",
qrSize = 1070,
qrPositionX,
qrPositionY = 2080,
title,
fileName = "Diálogos - QR code"
}) => {
setIsGenerating(true);
try {
const qrDataUrl = await QRCode.toDataURL(url, {
width: 1344,
margin: 2
});
const pdf = new jsPDF({
orientation,
unit: "mm",
format: "a4"
});
if (backgroundImagePath) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const bgImage = new Image();
bgImage.src = backgroundImagePath;
await new Promise((resolve) => {
bgImage.onload = async () => {
if (orientation === "landscape") {
canvas.width = 3508;
canvas.height = 2480;
} else {
canvas.width = 2480;
canvas.height = 3508;
}
ctx?.drawImage(bgImage, 0, 0, canvas.width, canvas.height);
const qrImage = new Image();
qrImage.src = qrDataUrl;
await new Promise((qrResolve) => {
qrImage.onload = () => {
const actualQrPositionX = qrPositionX ?? (canvas.width - qrSize) / 2;
const actualQrPositionY = orientation === "landscape" && qrPositionY === 2080 ? (canvas.height - qrSize) / 2 : qrPositionY;
ctx?.drawImage(
qrImage,
actualQrPositionX,
actualQrPositionY,
qrSize,
qrSize
);
qrResolve();
};
});
const finalImage = canvas.toDataURL("image/png");
if (orientation === "landscape") {
pdf.addImage(finalImage, "PNG", 0, 0, 297, 210);
} else {
pdf.addImage(finalImage, "PNG", 0, 0, 210, 297);
}
resolve();
};
});
} else {
if (title) {
pdf.setFontSize(24);
pdf.text(title, 105, 30, { align: "center" });
}
const simpleQrSize = 150;
pdf.addImage(
qrDataUrl,
"PNG",
(210 - simpleQrSize) / 2,
60,
simpleQrSize,
simpleQrSize
);
pdf.setFontSize(14);
pdf.text("Escaneie o QR Code", 105, 230, { align: "center" });
pdf.text(`URL: ${url}`, 105, 245, { align: "center" });
}
pdf.save(`${fileName}.pdf`);
notifications.show({
id: "qr-success",
title: "QR Code gerado com sucesso",
message: "O QR Code foi gerado e baixado",
color: "green"
});
} catch (error) {
console.error("Error generating QR code:", error);
notifications.show({
id: "qr-error",
title: "Erro ao gerar QR Code",
message: "Ocorreu um erro ao gerar o QR Code",
color: "red"
});
} finally {
setIsGenerating(false);
}
};
return {
isGenerating,
generateAndDownloadPDF
};
}
export {
FiltersProvider as F,
SelectedItemsProvider as S,
useFilters as a,
useSelectedItems as b,
useClipboard as c,
useDownloadBlob as d,
useMediaQuery as e,
usePaginationReset as f,
useQRCodeGenerator as g,
useSortableColumn as u
};
//# sourceMappingURL=use-qrcode-generator-D3CCg2u2.js.map