UNPKG

@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
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