@hmlr/govuk-react-components-library
Version:
These are common component use for React applications based on GDS and govuk-frontend
242 lines (230 loc) • 12 kB
JavaScript
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { useState, useRef, useCallback, useEffect } from 'react';
import { getDocument } from 'pdfjs-dist/webpack.mjs';
import { Link } from 'react-router-dom';
import 'govuk-frontend';
const Loading = ({ message = null, html = null }) => {
return (jsx("div", { className: "govuk-grid-row", children: jsxs("div", { className: "govuk-grid-column-full", children: [message || html ? (jsx("div", { className: "govuk-grid-row", children: jsx("div", { className: "govuk-grid-column-full head-space", children: html ? (html) : (jsx("h2", { className: "govuk-heading-m govuk-!-text-align-centre", children: message })) }) })) : null, jsx("div", { className: "govuk-hint govuk-grid-column-full", children: jsx("div", { className: "centered-wheel", "data-testid": "centered-wheel-identifier", children: jsx("div", { className: "loading-wheel-2", "data-testid": "loading-wheel-2-identifier" }) }) })] }) }));
};
function Slugify(str) {
str = str.replace(/^\s+|\s+$/g, ""); // trim leading/trailing white space
str = str.toLowerCase(); // convert string to lowercase
str = str
.replace(/[^a-z0-9 -]/g, "") // remove any non-alphanumeric characters
.replace(/\s+/g, "-") // replace spaces with hyphens
.replace(/-+/g, "-"); // remove consecutive hyphens
return str;
}
const _base64ToArrayBuffer = (base64) => {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
};
const isBase64 = (value) => /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/.test(value);
const ResolvePDFSource = (source) => {
const isBase64Source = isBase64(source);
if (!isBase64Source) {
return { isBase64Source, source };
}
const blob = new Blob([_base64ToArrayBuffer(source)], {
type: "application/pdf",
});
return { isBase64Source, source: URL.createObjectURL(blob) };
};
const LinkWithRef = ({ children, to, href, forwardedRef = null, ...attributes }) => {
if (to) {
return (jsx(Link, { ref: forwardedRef, to: to, ...attributes, children: children }));
}
return (jsx("a", { ref: forwardedRef, href: href || "#", ...attributes, children: children }));
};
const Button = (props) => {
const { element, href, to, isStartButton, disabled, className, preventDoubleClick, name, type, children, ...attributes } = props;
let el = "";
let buttonAttributes = {
name,
type,
...attributes,
"data-module": "govuk-button",
};
let buttonElement = null;
if (element) {
el = element;
}
else if (href || to) {
el = "a";
}
else {
el = "button";
}
let iconHtml;
if (isStartButton) {
iconHtml = (jsx("svg", { className: "govuk-button__start-icon", xmlns: "http://www.w3.org/2000/svg", width: "17.5", height: "19", viewBox: "0 0 33 40", "aria-hidden": "true", focusable: "false", children: jsx("path", { fill: "currentColor", d: "M0 0h13l20 20-20 20H0l20-20z" }) }));
}
const commonAttributes = {
className: `govuk-button ${className || ""}${disabled ? " govuk-button--disabled" : ""} ${isStartButton ? "govuk-button--start" : ""}`,
// ref: buttonRef,
};
if (preventDoubleClick) {
buttonAttributes["data-prevent-double-click"] = preventDoubleClick;
}
if (disabled) {
buttonAttributes = {
...buttonAttributes,
"aria-disabled": "true",
disabled: true,
};
}
if (el === "a") {
const linkAttributes = {
...commonAttributes,
role: "button",
draggable: "false",
...attributes,
"data-module": "govuk-button",
href,
to,
};
buttonElement = (jsxs(LinkWithRef, { ...linkAttributes, children: [children, iconHtml] }));
}
else if (el === "button") {
buttonElement = (jsxs("button", { ...buttonAttributes, ...commonAttributes, children: [children, iconHtml] }));
}
else if (el === "input") {
if (!type) {
buttonAttributes.type = "submit";
}
buttonElement = (jsx("input", { value: children, ...buttonAttributes, ...commonAttributes }));
}
return buttonElement;
};
Button.displayName = "Button";
function titleCase(str) {
return str
.toLowerCase()
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
const Label = ({ className = "", htmlFor, children, isPageHeading, ...attributes }) => {
if (!children)
return null;
const label = (jsx("label", { className: `govuk-label ${className}`, htmlFor: htmlFor, ...attributes, children: children }));
return isPageHeading ? (jsx("h1", { className: "govuk-label-wrapper", children: label })) : (label);
};
const DifferenceNavigation = ({ differenceId, setDifferenceFocus, totalDifferences, keyword = "variation", plural = "variations", }) => {
if (totalDifferences === 0) {
return (jsxs("p", { className: "govuk-body govuk-!-font-size-20 govuk-!-text-align-centre", children: ["No ", `${plural}`, " found"] }));
}
const isPreviousDisabled = differenceId <= 1;
const isNextDisabled = differenceId === totalDifferences;
const renderButton = (id, onClick, disabled, content) => (jsx(Button, { id: id, onClick: onClick, "data-testid": id, disabled: disabled, children: content }));
return (jsxs("div", { className: "govuk-grid-row", children: [jsx("div", { className: "govuk-grid-column-one-third", children: jsx("div", { className: "govuk-!-text-align-left", children: renderButton(`previous-${keyword}`, () => setDifferenceFocus(differenceId - 1), isPreviousDisabled, jsxs(Fragment, { children: [" ", jsx("svg", { className: "govuk-button__start-icon back-button", xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", transform: "rotate(180)", children: jsx("path", { fill: "currentColor", d: "M8.122 24l-4.122-4 8-8-8-8 4.122-4 11.878 12z" }) }), "\u00A0Previous", " "] })) }) }), jsx("div", { className: "govuk-grid-column-one-third", children: jsxs(Label, { className: "govuk-!-text-align-centre", children: [titleCase(keyword), " ", differenceId, " of ", totalDifferences] }) }), jsx("div", { className: "govuk-grid-column-one-third", children: jsx("div", { className: "govuk-!-text-align-right", children: renderButton(`next-${keyword}`, () => setDifferenceFocus(differenceId + 1), isNextDisabled, jsxs(Fragment, { children: [" ", "Next", jsx("svg", { className: "govuk-button__start-icon back-button", xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: jsx("path", { fill: "currentColor", d: "M8.122 24l-4.122-4 8-8-8-8 4.122-4 11.878 12z" }) })] })) }) })] }));
};
const ErrorMessage = ({ className, children, visuallyHiddenText = "Error", ...attributes }) => {
let visuallyHiddenTextComponent = null;
if (visuallyHiddenText) {
visuallyHiddenTextComponent = (jsxs("span", { className: "govuk-visually-hidden", children: [visuallyHiddenText, ": "] }));
}
return (jsxs("p", { className: `govuk-error-message ${className || ""}`, ...attributes, children: [visuallyHiddenTextComponent, children] }));
};
/**
* PDFViewerCanvas component for rendering PDF documents on a canvas.
* @param {PDFViewerCanvasProps} props - The component props
* @returns {JSX.Element} The rendered component
*/
const PDFViewerCanvas = ({ src, className, documentName, pageNumber = 1, showNavigation = true, ...attributes }) => {
const [pdfState, setPdfState] = useState({
loading: true,
numberOfPages: 0,
currentPage: pageNumber,
errorMessage: "",
});
const canvasRef = useRef(null);
const pdfDocumentRef = useRef(null);
const pageRenderingRef = useRef(false);
const pageNumberPendingRef = useRef(null);
const scale = 1;
const renderPage = useCallback((pageNum) => {
const pdfDocument = pdfDocumentRef.current;
const canvas = canvasRef.current;
if (!pdfDocument || !canvas)
return;
pageRenderingRef.current = true;
pdfDocument.getPage(pageNum).then((page) => {
const viewport = page.getViewport({ scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
const context = canvas.getContext("2d");
if (context) {
const renderContext = {
canvasContext: context,
viewport,
annotationMode: 3,
};
const renderTask = page.render(renderContext);
renderTask.promise.then(() => {
pageRenderingRef.current = false;
if (pageNumberPendingRef.current !== null) {
renderPage(pageNumberPendingRef.current);
pageNumberPendingRef.current = null;
}
});
}
});
}, [scale]);
const queueRenderPage = useCallback((pageNum) => {
if (pageRenderingRef.current) {
pageNumberPendingRef.current = pageNum;
}
else {
renderPage(pageNum);
}
}, [renderPage]);
const setDifferenceFocus = useCallback((pageNum) => {
if (pageNum < 1 || pageNum > pdfState.numberOfPages)
return;
setPdfState((prevState) => ({
...prevState,
currentPage: pageNum,
}));
queueRenderPage(pageNum);
}, [pdfState.numberOfPages, queueRenderPage]);
useEffect(() => {
const loadPDF = async (file) => {
try {
setPdfState((prevState) => ({ ...prevState, loading: true }));
const source = ResolvePDFSource(file).source;
const pdf = await getDocument(source).promise;
pdfDocumentRef.current = pdf;
const initialPageNumber = pdf.numPages >= pageNumber ? pageNumber : 1;
setPdfState({
loading: false,
numberOfPages: pdf.numPages,
currentPage: initialPageNumber,
errorMessage: "",
});
renderPage(initialPageNumber);
}
catch (error) {
console.error("Error loading PDF:", error);
setPdfState((prevState) => ({
...prevState,
loading: false,
errorMessage: documentName
? `There was an error loading the PDF document called "${documentName}".`
: "There was an error loading the PDF document.",
}));
}
};
if (src) {
loadPDF(src);
}
}, [src, pageNumber, renderPage]);
return (jsxs(Fragment, { children: [pdfState.loading && jsx(Loading, { message: "Loading PDF Document on Canvas" }), pdfState.errorMessage && (jsx(ErrorMessage, { className: "govuk-!-text-align-centre", children: pdfState.errorMessage })), showNavigation && pdfState.numberOfPages > 1 && (jsx("div", { style: { margin: "0 auto" }, children: jsx(DifferenceNavigation, { differenceId: pdfState.currentPage, setDifferenceFocus: setDifferenceFocus, totalDifferences: pdfState.numberOfPages, keyword: "page", plural: "Pages" }) })), jsx("canvas", { className: className, ref: canvasRef, id: `viewer-${Slugify(documentName || "")}`, "data-testid": "viewer", style: { width: "100%", height: "100%" }, ...attributes })] }));
};
export { PDFViewerCanvas };
//# sourceMappingURL=PDFViewerCanvas.esm.js.map