UNPKG

@utahdts/utah-design-system

Version:
1,453 lines 262 kB
(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@utahdts/utah-design-system-header"), require("react/jsx-runtime"), require("react"), require("@floating-ui/react-dom"), require("lodash-es"), require("use-immer"), require("date-fns"), require("uuid")) : typeof define === "function" && define.amd ? define(["exports", "@utahdts/utah-design-system-header", "react/jsx-runtime", "react", "@floating-ui/react-dom", "lodash-es", "use-immer", "date-fns", "uuid"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["@utahdts/utah-design-system"] = {}, global["@utahdts/utah-design-system-header"], global.jsxRuntime, global.React, global["@floating-ui/react-dom"], global["lodash-es"], global.useImmer, global.dateFns, global.uuid)); })(this, (function(exports2, utahDesignSystemHeader, jsxRuntime, React, reactDom, lodashEs, useImmer, dateFns, uuid) { "use strict"; const version$1 = "4.2.1"; const packageJson = { version: version$1 }; const popupPlacement = { BOTTOM: ( /** @type {PopupPlacement} */ "bottom" ), BOTTOM_START: ( /** @type {PopupPlacement} */ "bottom-start" ), BOTTOM_END: ( /** @type {PopupPlacement} */ "bottom-end" ), LEFT: ( /** @type {PopupPlacement} */ "left" ), LEFT_START: ( /** @type {PopupPlacement} */ "left-start" ), LEFT_END: ( /** @type {PopupPlacement} */ "left-end" ), RIGHT: ( /** @type {PopupPlacement} */ "right" ), RIGHT_START: ( /** @type {PopupPlacement} */ "right-start" ), RIGHT_END: ( /** @type {PopupPlacement} */ "right-end" ), TOP: ( /** @type {PopupPlacement} */ "top" ), TOP_START: ( /** @type {PopupPlacement} */ "top-start" ), TOP_END: ( /** @type {PopupPlacement} */ "top-end" ) }; const NO_POP_UP_TIMEOUT_MS = 350; const POP_UP_TIMEOUT_MS = 350; class PopupDelay { // timer ids #popupTimeoutId = NaN; #noPopupTimeoutId = NaN; // should popping occur immediately because the wait time has lapsed? #isImmediatePopup = false; /** wait a little while after a popup closes before turning off immediate popup flag */ startNoPopupTimer = () => { clearTimeout(this.#noPopupTimeoutId); clearTimeout(this.#popupTimeoutId); if (this.#isImmediatePopup) { this.#noPopupTimeoutId = window.setTimeout( () => { this.#isImmediatePopup = false; }, NO_POP_UP_TIMEOUT_MS ); } }; /** * wait to pop unless the popping period has already lapsed * Make sure to call startNoPopupTimer when the popup goes away * @param {() => void} callback function to call when waiting is done */ startPopupTimer = (callback) => { clearTimeout(this.#noPopupTimeoutId); clearTimeout(this.#popupTimeoutId); if (this.#isImmediatePopup) { callback(); } else { this.#popupTimeoutId = window.setTimeout( () => { this.#isImmediatePopup = true; callback(); }, POP_UP_TIMEOUT_MS ); } }; } const POPUP_DELAY = new PopupDelay(); function usePopupDelay() { return POPUP_DELAY; } function joinClassNames(...classNames) { return ( // convert passed in parameters to an array lodashEs.castArray(Array.from(classNames)).flat(Infinity).map((className) => typeof className === "string" ? lodashEs.trim(className) : className).filter(lodashEs.identity).join(" ") ); } function Tooltip({ children, className, innerRef: draftInnerRef, isPopupVisible, offset = 5, placement = popupPlacement.BOTTOM, referenceElement: draftReferenceElement }) { const [isPopupVisibleInternal, setIsPopupVisibleInternal] = React.useState(false); const [popupElement, setPopupElement] = ( /** @type {typeof useState<HTMLDivElement | null>} */ React.useState(null) ); const [arrowElement, setArrowElement] = ( /** @type {typeof useState<HTMLDivElement | null>} */ React.useState(null) ); const { floatingStyles, middlewareData } = reactDom.useFloating({ elements: { reference: draftReferenceElement, floating: popupElement }, middleware: [ reactDom.offset(offset), reactDom.flip(), reactDom.shift(), reactDom.arrow({ element: arrowElement }) ], open: !(isPopupVisible ?? isPopupVisibleInternal), placement, whileElementsMounted: reactDom.autoUpdate }); const { startNoPopupTimer, startPopupTimer } = usePopupDelay(); const onEscape = (e) => { if (e.code === "Escape" || e.key === "Escape") { setIsPopupVisibleInternal(false); document.removeEventListener("keyup", onEscape); } }; React.useEffect( () => { if (draftReferenceElement && isPopupVisible === void 0) { if (draftReferenceElement.onmouseenter) { throw new Error("ToolTip: onMouseEnter previously set"); } if (draftReferenceElement.onmouseleave) { throw new Error("ToolTip: onMouseLeave previously set"); } if (draftReferenceElement.onfocus) { throw new Error("ToolTip: onfocus previously set"); } if (draftReferenceElement.onblur) { throw new Error("ToolTip: onblur previously set"); } draftReferenceElement.onmouseenter = () => startPopupTimer(() => { setIsPopupVisibleInternal(true); }); draftReferenceElement.onfocus = () => { setIsPopupVisibleInternal(true); }; draftReferenceElement.onmouseleave = () => { startNoPopupTimer(); setIsPopupVisibleInternal(false); }; draftReferenceElement.onblur = () => setIsPopupVisibleInternal(false); document.addEventListener("keyup", onEscape); } return (() => { if (draftReferenceElement) { draftReferenceElement.onmouseenter = null; draftReferenceElement.onmouseleave = null; draftReferenceElement.onfocus = null; draftReferenceElement.onblur = null; } }); }, [draftReferenceElement, isPopupVisible, startNoPopupTimer, startPopupTimer, onEscape] ); return /* @__PURE__ */ jsxRuntime.jsx( "div", { ref: (refValue) => { setPopupElement(refValue); if (draftInnerRef) { draftInnerRef.current = refValue; } }, style: floatingStyles, className: joinClassNames( className, "tooltip__wrapper", !(isPopupVisible ?? isPopupVisibleInternal) && "tooltip__wrapper--hidden" ), "aria-hidden": "true", "data-popup-placement": middlewareData?.offset?.placement || placement, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tooltip__content", children: [ children, /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setArrowElement, style: { left: middlewareData.arrow?.x, top: middlewareData.arrow?.y }, className: "tooltip__arrow" }) ] }) } ); } const BUTTON_APPEARANCE = { SOLID: ( /** @type {ButtonAppearance} */ "solid" ), OUTLINED: ( /** @type {ButtonAppearance} */ "outlined" ) }; const BUTTON_TYPES = { BUTTON: ( /** @type {ButtonTypes} */ "button" ), RESET: ( /** @type {ButtonTypes} */ "reset" ), SUBMIT: ( /** @type {ButtonTypes} */ "submit" ) }; const ICON_BUTTON_APPEARANCE = { SOLID: ( /** @type {IconButtonAppearance} */ "solid" ), OUTLINED: ( /** @type {IconButtonAppearance} */ "outlined" ), BORDERLESS: ( /** @type {IconButtonAppearance} */ "borderless" ) }; const componentColors = { PRIMARY: ( /** @type {ComponentColors} */ "primary" ), SECONDARY: ( /** @type {ComponentColors} */ "secondary" ), ACCENT: ( /** @type {ComponentColors} */ "accent" ), NONE: ( /** @type {ComponentColors} */ "none" ) }; const formElementSizesEnum = { SMALL3X: ( /** @type {FormElementSizes} */ "small3x" ), SMALL2X: ( /** @type {FormElementSizes} */ "small2x" ), SMALL1X: ( /** @type {FormElementSizes} */ "small1x" ), SMALL: ( /** @type {FormElementSizes} */ "small" ), MEDIUM: ( /** @type {FormElementSizes} */ "medium" ), LARGE: ( /** @type {FormElementSizes} */ "large" ), LARGE1X: ( /** @type {FormElementSizes} */ "large1x" ) }; function handleEvent(func) { return (e) => { if (e.preventDefault) { e.preventDefault(); } if (e.stopPropagation) { e.stopPropagation(); } if (e?.nativeEvent?.stopImmediatePropagation) { e.nativeEvent.stopImmediatePropagation(); } func.call(e.target, e); }; } function Spinner({ children, className, id, innerRef, size = 60, strokeWidth = 10, value = NaN, ...rest }) { const strokeWidthUse = Number.isNaN(strokeWidth) ? 10 : strokeWidth; const strokeWidthPlus1Use = Number.isNaN(strokeWidth) ? 10 : (strokeWidth ?? 0) + 1; const widthUse = Number.isNaN(size) ? void 0 : size; return /* @__PURE__ */ jsxRuntime.jsxs( "div", { "aria-valuemax": 100, "aria-valuemin": 0, "aria-valuenow": Number.isNaN(value) ? void 0 : (value ?? 0) * 100, className: joinClassNames( className, "spinner", Number.isNaN(value) ? "spinner--indeterminate" : "spinner--determinate" ), id: id ?? void 0, ref: innerRef, role: "progressbar", "aria-label": Number.isNaN(value) ? "Loading..." : `Loading ${(value ?? 0) * 100}% complete`, ...rest, children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__animation", children: /* @__PURE__ */ jsxRuntime.jsxs( "svg", { height: widthUse, role: "presentation", viewBox: "-10.00 -10.00 120.00 120.00", width: widthUse, children: [ /* @__PURE__ */ jsxRuntime.jsx( "path", { strokeWidth: strokeWidthUse, className: "spinner__track", d: "M 50,50 m 0,-45 a 45,45 0 1 1 0,90 a 45,45 0 1 1 0,-90" } ), /* @__PURE__ */ jsxRuntime.jsx( "path", { className: "spinner__value", d: "M 50,50 m 0,-45 a 45,45 0 1 1 0,90 a 45,45 0 1 1 0,-90", pathLength: "360", strokeDasharray: "360 360", strokeDashoffset: 360 * (1 - (Number.isNaN(value) ? 0.25 : value ?? 0)), strokeWidth: strokeWidthPlus1Use } ) ] } ) }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__children", children }) ] } ); } function Button({ appearance = BUTTON_APPEARANCE.OUTLINED, children, className, color = componentColors.NONE, innerRef, isBusy, iconLeft, iconRight, isDisabled, id, onClick, size = "medium", type = BUTTON_TYPES.BUTTON, ...rest }) { return /* @__PURE__ */ jsxRuntime.jsxs( "button", { className: joinClassNames( "button", className, `button--${appearance}`, // default color is none color && color !== componentColors.NONE ? `button--${color}-color` : null, // default size is medium size && size !== formElementSizesEnum.MEDIUM ? `button--${size}` : null ), disabled: isDisabled || isBusy, id, onClick: handleEvent((e) => onClick?.(e)), ref: innerRef, type, ...rest, children: [ iconLeft ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "button--icon button--icon-left", children: iconLeft }) : null, children, isBusy ? /* @__PURE__ */ jsxRuntime.jsx( Spinner, { className: "ml-spacing-xs", size: size === formElementSizesEnum.LARGE1X ? 24 : 22, strokeWidth: size === formElementSizesEnum.LARGE1X ? 14 : 12 } ) : null, iconRight ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "button--icon button--icon-right", children: iconRight }) : null ] } ); } function ClickableTag({ children, className, id, iconLeft, iconRight, innerRef, isDisabled, isSelected, onClick, size = formElementSizesEnum.MEDIUM, ...rest }) { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tag__wrapper", ref: innerRef, children: /* @__PURE__ */ jsxRuntime.jsxs( "button", { "aria-pressed": isSelected, className: joinClassNames( "tag", "tag__button", `tag--${size}`, className, isSelected ? "tag--selected" : "" ), disabled: isDisabled, id, onClick: onClick && handleEvent((e) => onClick(e)), type: "button", ...rest, children: [ iconLeft ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tag--icon tag--icon-left", children: iconLeft }) : null, children, iconRight ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tag--icon tag--icon-right", children: iconRight }) : null ] } ) }); } const ConfirmationButtonContext = React.createContext(false); function ConfirmationButtonContextProvider({ children, isClicked }) { return /* @__PURE__ */ jsxRuntime.jsx(ConfirmationButtonContext.Provider, { value: isClicked, children }); } function ConfirmationButton({ appearance = BUTTON_APPEARANCE.OUTLINED, children, className, color = componentColors.NONE, confirmationColor, id, innerRef, isBusy, isDisabled, onClick, size = "medium", type = BUTTON_TYPES.BUTTON, ...rest }) { const [isClicked, setIsClicked] = React.useState(false); const resetButton = React.useCallback(() => { setIsClicked(false); }, []); const handleOnClick = handleEvent((e) => { if (!isBusy) { if (isClicked) { onClick?.(e); resetButton(); } else { setIsClicked(true); } } }); const onClickCallback = React.useCallback(handleOnClick, [handleOnClick]); return /* @__PURE__ */ jsxRuntime.jsxs( "button", { className: joinClassNames( "button", className, `button--${appearance}`, // default color is none color && color !== "none" && !(isClicked && confirmationColor) ? `button--${color}-color` : null, // default size is medium size && size !== formElementSizesEnum.MEDIUM ? `button--${size}` : null, isClicked ? "button--confirm" : null, isClicked && confirmationColor ? `button--${confirmationColor}-color` : null ), disabled: isDisabled || isBusy, id, ref: innerRef, onClick: onClickCallback, onBlur: resetButton, onKeyUp: handleKeyPress("Escape", resetButton), type, ...rest, children: [ /* @__PURE__ */ jsxRuntime.jsx(ConfirmationButtonContextProvider, { isClicked, children }), isBusy ? /* @__PURE__ */ jsxRuntime.jsx( Spinner, { className: "ml-spacing-xs", size: size === formElementSizesEnum.LARGE1X ? 24 : 22, strokeWidth: size === formElementSizesEnum.LARGE1X ? 14 : 12 } ) : null ] } ); } function useConfirmationButtonContext() { return React.useContext(ConfirmationButtonContext); } function ConfirmationChildren({ children }) { const isClicked = useConfirmationButtonContext(); return isClicked ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : null; } function InitialChildren({ children }) { const isClicked = useConfirmationButtonContext(); return isClicked ? null : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }); } function IconButton({ appearance = ICON_BUTTON_APPEARANCE.OUTLINED, children, className, color = componentColors.NONE, icon, id, innerRef: draftInnerRef, isDisabled, isTitleVisible, onClick, size = "medium", title, tooltipText, ...rest }) { const [referenceElement, setReferenceElement] = ( /** @type {typeof useState<HTMLButtonElement | null>} */ React.useState(null) ); if (draftInnerRef && referenceElement) { draftInnerRef.current = referenceElement; } return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsxs( "button", { className: joinClassNames( "button icon-button", className, `${appearance === ICON_BUTTON_APPEARANCE.BORDERLESS ? "icon-button--" : "button--"}${appearance}`, // default color is none color && color !== "none" ? `button--${color}-color` : null, isTitleVisible ? "icon-button--visible-title" : null, // default size is medium size && size !== formElementSizesEnum.MEDIUM ? `icon-button--${size}` : null ), disabled: isDisabled, id: id || void 0, onClick, ref: setReferenceElement, type: "button", ...rest, children: [ icon, /* @__PURE__ */ jsxRuntime.jsx("span", { className: isTitleVisible ? void 0 : "visually-hidden", children: title }), children ] } ), referenceElement ? /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { referenceElement, children: tooltipText ?? title }) : null ] }); } function Tag({ children, className, clearMessage = "Clear Tag", iconButtonProps = {}, innerRef, iconLeft, iconRight, isDisabled, id, onClear, size = formElementSizesEnum.MEDIUM, ...rest }) { return /* @__PURE__ */ jsxRuntime.jsxs( "div", { className: joinClassNames("tag__wrapper", onClear && "tag--clearable"), ref: innerRef, ...rest, children: [ /* @__PURE__ */ jsxRuntime.jsxs( "span", { className: joinClassNames("tag", className, `tag--${size}`), id, children: [ iconLeft ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tag--icon tag--icon-left", children: iconLeft }) : null, children, iconRight ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tag--icon tag--icon-right", children: iconRight }) : null ] } ), onClear ? /* @__PURE__ */ jsxRuntime.jsx( IconButton, { className: "tag__clear-button icon-button--borderless icon-button--small1x", icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "utds-icon-before-x-icon", "aria-hidden": true }), onClick: onClear, title: clearMessage, isDisabled, ...iconButtonProps } ) : null ] } ); } function Accordion({ children, className, contentClassName, headingLevel = 2, headerClassName, headerContent, id, isOpen, onToggle }) { const [stateIsOpen, setStateIsOpen] = React.useState(isOpen || false); React.useEffect(() => { setStateIsOpen(!!isOpen); }, [isOpen]); function toggleAccordion() { if (onToggle) { onToggle(stateIsOpen); } else { setStateIsOpen(!stateIsOpen); } } const HeadingTag = `h${headingLevel}`; return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: joinClassNames(["accordion", className]), id, children: [ /* @__PURE__ */ jsxRuntime.jsxs( "button", { "aria-expanded": stateIsOpen, "aria-controls": `accordion-content__${id}`, className: joinClassNames(["accordion__header", headerClassName, stateIsOpen ? "accordion__header--open" : ""]), id: `accordion-button__${id}`, onClick: handleEvent(toggleAccordion), type: "button", children: [ /* @__PURE__ */ jsxRuntime.jsx(HeadingTag, { children: headerContent }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: `utds-icon-before-circle-chevron-up icon-button__icon ${stateIsOpen ? "" : "icon-button__icon--rotate180"}`, "aria-hidden": "true" }) ] } ), /* @__PURE__ */ jsxRuntime.jsx( "div", { "aria-hidden": !stateIsOpen, "aria-labelledby": `accordion-button__${id}`, className: joinClassNames(["accordion__content", contentClassName, stateIsOpen ? "accordion__content--open" : ""]), id: `accordion-content__${id}`, role: "region", children } ) ] }); } const UtahDesignSystemContext = ( /** @type {typeof createContext<ImmerHookUtahDesignSystemContext>} */ React.createContext([ { ariaLive: { assertiveMessages: [], politeMessages: [] }, banners: [] }, // eslint-disable-next-line @typescript-eslint/no-empty-function () => { } ]) ); function useUtahDesignSystemContext() { return React.useContext(UtahDesignSystemContext); } function useAriaMessaging() { const [, setState] = useUtahDesignSystemContext(); const addPoliteMessage = React.useCallback( /** * @param {string} message * @returns {void} */ (message) => { setState((draftState) => { draftState.ariaLive.politeMessages.push(message); }); }, [setState] ); const addAssertiveMessage = React.useCallback( /** * @param {string} message * @returns {void} */ (message) => { setState((draftState) => { draftState.ariaLive.assertiveMessages.push(message); }); }, [setState] ); return React.useMemo(() => ({ addAssertiveMessage, addPoliteMessage }), [addAssertiveMessage, addPoliteMessage]); } function getFocusableElements(element) { return [ ...element.querySelectorAll('a[href], area[href], button, input, textarea, select, object, [tabindex]:not([tabindex="-1"])') ].filter((item) => !item.hasAttribute("disabled")); } function useHandleEscape(onEscape) { return React.useCallback( (e) => { if (e.code === "Escape" || e.key === "Escape") { e.preventDefault(); e.stopPropagation(); if (onEscape) { onEscape(e); } } }, [onEscape] ); } function useHandleTab(firstTabElement, lastTabElement) { return React.useCallback((e) => { if (e.code === "Tab" || e.key === "Tab") { if (e.shiftKey) { if (document.activeElement === firstTabElement) { lastTabElement?.focus(); e.preventDefault(); } } else if (document.activeElement === lastTabElement) { firstTabElement?.focus(); e.preventDefault(); } } }, [firstTabElement, lastTabElement]); } const DRAWER_PLACEMENT = { RIGHT: ( /** @type {DrawerPlacement} */ "drawer--right" ), LEFT: ( /** @type {DrawerPlacement} */ "drawer--left" ) }; function Drawer({ ariaLabelledBy, children, className, id, innerRef, onClose, onEscape, position = DRAWER_PLACEMENT.RIGHT }) { const ref = ( /** @type {typeof useRef<HTMLDialogElement | null>} */ React.useRef(null) ); const [lastActiveElement] = useImmer.useImmer( /** @type {HTMLElement | undefined} */ document.activeElement ); const [firstTabElement, setFirstTabElement] = useImmer.useImmer( /** @type {HTMLElement | undefined} */ void 0 ); const [lastTabElement, setLastTabElement] = useImmer.useImmer( /** @type {HTMLElement | undefined} */ void 0 ); const { addAssertiveMessage } = useAriaMessaging(); const handleEscape = useHandleEscape(onEscape); const handleTab = useHandleTab(firstTabElement, lastTabElement); React.useEffect(() => { if (ref.current) { const list = getFocusableElements(ref.current); if (list.length) { const firstElement = list[0]; setFirstTabElement(firstElement); const lastElement = list[list.length - 1]; setLastTabElement(lastElement); firstElement?.focus(); } else { console.warn("No focusable element found. Make sure to include a way to close the drawer."); } } }, []); React.useEffect(() => () => { addAssertiveMessage("Closing drawer."); lastActiveElement?.focus(); }, []); return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "drawer-wrapper", ref: innerRef, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "drawer__backdrop backdrop-dark", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs( "dialog", { "aria-labelledby": ariaLabelledBy, className: joinClassNames("drawer__inner", position, className), id, onClick: (e) => e.stopPropagation(), onKeyDown: handleTab, onKeyUp: handleEscape, ref, children: [ children, onClose ? /* @__PURE__ */ jsxRuntime.jsx( IconButton, { appearance: ICON_BUTTON_APPEARANCE.BORDERLESS, className: "drawer__close-button", icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "utds-icon-before-x-icon", "aria-hidden": "true" }), onClick: onClose, size: "small", title: "Close" } ) : void 0 ] } ) }) }); } function DrawerContent({ children, className, id }) { return /* @__PURE__ */ jsxRuntime.jsx( "div", { className: joinClassNames("drawer__content", className), id, children } ); } function DrawerFooter({ children, className, id }) { return /* @__PURE__ */ jsxRuntime.jsx( "div", { className: joinClassNames("drawer__footer", className), id, children } ); } function DrawerTitle({ children, className, id, tagName: TagName = "div" }) { return ( // @ts-expect-error /* @__PURE__ */ jsxRuntime.jsx( TagName, { className: joinClassNames("drawer__title", className), id, children } ) ); } const TabGroupContext = ( /** @type {typeof createContext<TabGroupContextValue>} */ React.createContext({ isVertical: false, navigateNext: () => { }, navigatePrevious: () => { }, registerTab: () => { }, selectedTabId: "", setSelectedTabId: () => { }, tabGroupId: "", unRegisterTab: () => { } }) ); function useTabGroupContext() { return React.useContext(TabGroupContext); } function generateTabId(tabGroupId, tabId) { return `tab__${tabGroupId}__${tabId}`; } function Tab({ children, id }) { const tabRef = React.useRef( /** @type {HTMLButtonElement | null} */ null ); const { isVertical, navigateNext, navigatePrevious, registerTab, selectedTabId, setSelectedTabId, tabGroupId, unRegisterTab } = useTabGroupContext(); const onKeyChange = (event) => { if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)) { event.preventDefault(); } switch (event.key) { case "ArrowLeft": if (!isVertical) { navigatePrevious(); } break; case "ArrowRight": if (!isVertical) { navigateNext(); } break; case "ArrowUp": if (isVertical) { navigatePrevious(); } break; case "ArrowDown": if (isVertical) { navigateNext(); } break; } }; React.useEffect(() => { if (tabRef) { registerTab(tabRef); } }, []); React.useEffect( () => (() => { unRegisterTab(tabRef); }), [] ); return /* @__PURE__ */ jsxRuntime.jsx( "div", { className: joinClassNames( selectedTabId === id && "tab-group__tab--selected", "tab-group__tab" ), role: "presentation", children: /* @__PURE__ */ jsxRuntime.jsx( "button", { "aria-controls": `tabpanel__${tabGroupId}__${id}`, "aria-selected": selectedTabId === id, className: joinClassNames( selectedTabId === id && "tab-group__tab-button--selected", "tab-group__tab-button" ), id: generateTabId(tabGroupId, id), onClick: handleEvent(() => setSelectedTabId(id)), onKeyDown: onKeyChange, ref: tabRef, role: "tab", tabIndex: selectedTabId === id ? 0 : -1, type: "button", children } ) } ); } function TabGroup({ children, className, defaultValue, isVertical, onChange, value }) { const tabGroupId = React.useId(); const tabGroupRef = React.useRef( /** @type {HTMLDivElement | null} */ null ); const [tabGroupState, setTabGroupState] = useImmer.useImmer(() => ({ selectedTabId: defaultValue || "", tabGroupId, tabs: ( /** @type {NodeList | []} */ [] ) })); const navigateTab = React.useCallback((tab) => { if (tab) { tab?.focus(); tab?.click(); } }, []); const findCurrentTabIndex = React.useCallback( () => tabGroupState.tabs.findIndex((tab) => tab?.id === generateTabId(tabGroupState.tabGroupId, tabGroupState.selectedTabId)), [tabGroupState] ); const registerTab = React.useCallback( (tab) => { setTabGroupState((draftState) => { const checkTab = draftState.tabs.find((tabSearch) => tabSearch?.id === tab?.current?.id); if (checkTab) { Object.assign(checkTab, tab?.current); } else { draftState.tabs.push(tab?.current); } }); }, [tabGroupState, setTabGroupState] ); const unRegisterTab = React.useCallback( (tab) => { setTabGroupState((draftState) => { draftState.tabs = draftState.tabs.filter((filterTab) => filterTab?.id !== tab?.current?.id); }); }, [] ); React.useEffect( () => { if (value !== void 0) { setTabGroupState((draftState) => { draftState.selectedTabId = value; }); } }, [value] ); const contextValue = React.useMemo( () => ({ isVertical: !!isVertical, navigateNext() { const index = findCurrentTabIndex(); const nextIndex = (index + 1) % tabGroupState.tabs.length; navigateTab(tabGroupState?.tabs?.[nextIndex] || null); }, navigatePrevious() { const index = findCurrentTabIndex(); const nextIndex = (index + tabGroupState.tabs.length - 1) % tabGroupState.tabs.length; navigateTab(tabGroupState?.tabs?.[nextIndex] || null); }, registerTab, selectedTabId: value || tabGroupState.selectedTabId || "", setSelectedTabId(tabId) { if (onChange) { onChange(tabId); } else { setTabGroupState((draftState) => { draftState.selectedTabId = tabId; }); } }, tabGroupId: tabGroupState.tabGroupId, unRegisterTab }), [tabGroupState, isVertical] ); return /* @__PURE__ */ jsxRuntime.jsx(TabGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: joinClassNames("tab-group", className, isVertical && "tab-group--vertical"), id: `tab-group__${tabGroupState.tabGroupId}`, ref: tabGroupRef, children } ) }); } function TabGroupTitle({ children, className, tagName: TagName = "div" }) { const { tabGroupId } = useTabGroupContext(); return ( // @ts-expect-error /* @__PURE__ */ jsxRuntime.jsx(TagName, { id: `tab-group__${tabGroupId}`, className: joinClassNames("tag-group__title", className), children }) ); } function TabList({ children, className }) { const { isVertical } = useTabGroupContext(); return /* @__PURE__ */ jsxRuntime.jsx( "div", { className: joinClassNames(className, "tab-group__list"), role: "tablist", "aria-orientation": isVertical ? "vertical" : "horizontal", children } ); } function TabPanel({ children, className, tabId }) { const { selectedTabId, tabGroupId } = useTabGroupContext(); return /* @__PURE__ */ jsxRuntime.jsx( "div", { "aria-labelledby": `tab__${tabGroupId}__${tabId}`, className: joinClassNames( className, selectedTabId !== tabId && "visually-hidden", "tab-group__panel" ), id: `tabpanel__${tabGroupId}__${tabId}`, role: "tabpanel", tabIndex: selectedTabId === tabId ? 0 : -1, children } ); } function TabPanels({ children }) { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tab-group__panels", role: "presentation", children }); } function FooterAgencyInformation({ children }) { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "utah-design-system", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information", children }) }); } function FooterAgencyInformationColumn({ children }) { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information__column", children }); } function FooterAgencyInformationInfo({ agencyTitleFirstLine, agencyTitleSecondLine, address, email, logo, phone }) { return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "footer-agency-information__title-wrapper", children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information__title-image", children: logo }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "footer-agency-information__title", children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information__first-line", children: agencyTitleFirstLine }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information__second-line", children: agencyTitleSecondLine }) ] }) ] }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "footer-agency-information__address", children: [ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "footer-agency-information__address-street-address-1", children: address.streetAddress1 }), /* @__PURE__ */ jsxRuntime.jsx("br", {}), address.streetAddress2 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "footer-agency-information__address-street-address-2", children: address.streetAddress2 }), /* @__PURE__ */ jsxRuntime.jsx("br", {}) ] }) : void 0, /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "footer-agency-information__address-city-state-zip", children: [ address.city, ", ", address.state, " ", address.zipCode ] }) ] }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information__email", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${email}`, children: email }) }), phone && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-agency-information__phone", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${phone}`, children: phone }) }) ] }); } function FooterSocialMediaBar({ children, title = "Follow us online" }) { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "utah-design-system", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "footer-social-media-bar", children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-social-media-bar__follow-us", children: title }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "footer-social-media-bar__icon-bar", children }) ] }) }); } function useOnKeyUp(targetKey, func, stopPropagation) { return React.useCallback( (e) => { const isMatchingKey = e.key === targetKey; if (isMatchingKey) { if (stopPropagation) { e.stopPropagation(); e.preventDefault(); } func(e); } return isMatchingKey; }, [func, stopPropagation, targetKey] ); } function ErrorMessage({ errorMessage, id }) { return errorMessage ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "input-wrapper__error-message", id: `error__${id}`, children: errorMessage }) : null; } function RequiredStar() { return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "required-star", "aria-hidden": true, children: "*" }); } function dateIsEqualYM(dateA, dateB) { return dateA.getFullYear() - dateB.getFullYear() || dateA.getMonth() - dateB.getMonth(); } function dateIsEqualYMD(dateA, dateB) { return !!dateA && !!dateB && dateA.getFullYear() === dateB.getFullYear() && dateA.getMonth() === dateB.getMonth() && dateA.getDate() === dateB.getDate(); } function constructCalendarGridValue(infoDate, focusDate, selectedDate, viewedMonthDate) { return { date: infoDate, isFocusDate: dateIsEqualYMD(infoDate, [focusDate, selectedDate].find((testDate) => testDate && dateFns.isValid(testDate)) ?? /* @__PURE__ */ new Date()), isNextMonth: dateFns.add(viewedMonthDate, { months: 1 }).getMonth() === infoDate.getMonth(), isPreviousMonth: dateFns.add(viewedMonthDate, { months: -1 }).getMonth() === infoDate.getMonth(), isSelectedDate: dateIsEqualYMD(infoDate, selectedDate), isTodayDate: dateIsEqualYMD(infoDate, /* @__PURE__ */ new Date()) }; } function calendarGrid(focusDate, selectedDate) { if (Number.isNaN(focusDate)) { throw new Error("calendarGrid: focusDate is invalid"); } if (Number.isNaN(selectedDate)) { throw new Error("calendarGrid: selectedDate is invalid"); } const viewedMonthDate = focusDate && dateFns.isValid(focusDate) ? focusDate : /* @__PURE__ */ new Date(); const firstOfMonth = new Date(viewedMonthDate.getFullYear(), viewedMonthDate.getMonth(), 1); const startDayOfWeek = Number(dateFns.format(firstOfMonth, "e")); const calendarGridMonth = []; for ( let loopDate = dateFns.add(firstOfMonth, { days: -1 * startDayOfWeek + 1 }); // work all the way through the viewed month dates dateIsEqualYM(loopDate, viewedMonthDate) <= 0 || calendarGridMonth[calendarGridMonth.length - 1]?.length !== 7; loopDate = dateFns.add(loopDate, { days: 1 }) ) { if (calendarGridMonth.length === 0 || calendarGridMonth[calendarGridMonth.length - 1]?.length === 7) { calendarGridMonth.push([]); } calendarGridMonth[calendarGridMonth.length - 1]?.push(constructCalendarGridValue(loopDate, focusDate, selectedDate, viewedMonthDate)); } return calendarGridMonth; } let oldMoveCurrentValueFocusTimeoutId = NaN; function moveCurrentValueFocus(calendarInputId, oldDate, dateFormat, duration) { const newDate = dateFns.add(oldDate && dateFns.isValid(oldDate) ? oldDate : /* @__PURE__ */ new Date(), duration); clearTimeout(oldMoveCurrentValueFocusTimeoutId); oldMoveCurrentValueFocusTimeoutId = window.setTimeout( () => { document.getElementById(`calendar-input__${calendarInputId}__${dateFns.format(newDate, dateFormat)}`)?.focus(); }, 0 ); return newDate; } function CalendarInput({ className, dateFormat = "MM/dd/yyyy", errorMessage, id, innerRef, isDisabled, isHidden, isRequired, label, labelClassName, onChange, shouldSetFocusOnMount, showTodayButton, value, wrapperClassName, ...rest }) { const { addPoliteMessage } = useAriaMessaging(); const calendarInputId = React.useId(); const firstFocusableElementRef = React.useRef( /** @type {any | null} */ null ); const currentValueDate = value ? dateFns.parse(value, dateFormat, /* @__PURE__ */ new Date()) : null; const [currentValueDateInternal, setCurrentValueDateInternal] = React.useState( /** @type {Date | null} */ null ); React.useEffect( () => { if (currentValueDateInternal?.getTime() !== currentValueDate?.getTime()) { setCurrentValueDateInternal(currentValueDate && dateFns.isValid(currentValueDate) ? currentValueDate : /* @__PURE__ */ new Date()); } }, [currentValueDate?.getTime()] ); React.useEffect( () => { if (shouldSetFocusOnMount && !isHidden) { firstFocusableElementRef.current?.focus(); } }, [shouldSetFocusOnMount, isHidden] ); const calendarMonthDate = currentValueDateInternal && dateFns.isValid(currentValueDateInternal) ? currentValueDateInternal : /* @__PURE__ */ new Date(); const calendarGridValues = React.useMemo(() => calendarGrid(currentValueDateInternal, currentValueDate), [currentValueDateInternal, value]); const onDownArrowPress = useOnKeyUp( "ArrowDown", React.useCallback(() => setCurrentValueDateInternal((date) => moveCurrentValueFocus(calendarInputId, date, dateFormat, { weeks: 1 })), []), true ); const onUpArrowPress = useOnKeyUp( "ArrowUp", React.useCallback(() => setCurrentValueDateInternal((date) => moveCurrentValueFocus(calendarInputId, date, dateFormat, { weeks: -1 })), []), true ); const onLeftArrowPress = useOnKeyUp( "ArrowLeft", React.useCallback(() => setCurrentValueDateInternal((date) => moveCurrentValueFocus(calendarInputId, date, dateFormat, { days: -1 })), []), true ); const onRightArrowPress = useOnKeyUp( "ArrowRight", React.useCallback(() => setCurrentValueDateInternal((date) => moveCurrentValueFocus(calendarInputId, date, dateFormat, { days: 1 })), []), true ); return /* @__PURE__ */ jsxRuntime.jsxs( "div", { className: joinClassNames("input-wrapper input-wrapper--calendar-input", wrapperClassName, className), ref: innerRef, ...rest, children: [ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: id, className: labelClassName ?? void 0, children: [ label, isRequired ? /* @__PURE__ */ jsxRuntime.jsx(RequiredStar, {}) : null ] }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-input__controls", children: [ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-input__controls-month", children: [ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [ shouldSetFocusOnMount ? /* @__PURE__ */ jsxRuntime.jsx( "div", { "aria-label": "You are in a calendar date picker. Press tab to interact. Use arrow keys on days to navigate.", className: "calendar-input__first-focusable-element", ref: firstFocusableElementRef, tabIndex: isHidden ? -1 : 0 } ) : null, /* @__PURE__ */ jsxRuntime.jsx( IconButton, { className: "icon-button--small1x icon-button--borderless", icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "utds-icon-before-chevron-left", "aria-hidden": "true" }), innerRef: shouldSetFocusOnMount ? void 0 : firstFocusableElementRef, isDisabled, onClick: () => setCurrentValueDateInternal((draftDate) => { const newDate = dateFns.add(draftDate && dateFns.isValid(draftDate) ? draftDate : /* @__PURE__ */ new Date(), { months: -1 }); addPoliteMessage(`Month has changed to ${dateFns.format(newDate, "MMMM yyyy")}`); return newDate; }), title: "Previous Month", tabIndex: isHidden ? -1 : 0 } ) ] }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "calendar-input__month", children: dateFns.format(calendarMonthDate, "MMMM") }), /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx( IconButton, { className: "icon-button--small1x icon-button--borderless", icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "utds-icon-before-chevron-right", "aria-hidden": "true" }), isDisabled, onClick: () => setCurrentValueDateInternal((draftDate) => { const newDate = dateFns.add(draftDate && dateFns.isValid(draftDate) ? draftDate : /* @__PURE__ */ new Date(), { months: 1 }); addPoliteMessage(`Month has changed to ${dateFns.format(newDate, "MMMM yyyy")}`); return newDate; }), title: "Next Month", tabIndex: isHidden ? -1 : 0 } ) }) ] }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-input__controls-year", children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx( IconButton, { className: "icon-button--small1x icon-button--borderless", icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "utds-icon-before-double-arrow-left", "aria-hidden": "true" }), isDisabled, onClick: () => setCurrentValueDateInternal((draftDate) => { const newDate = dateFns.add(draftDate && dateFns.isValid(draftDate) ? draftDate : /* @__PURE__ */ new Date(), { years: -1 }); addPoliteMessage(`Year has changed to ${newDate.getFullYear()}`); return newDate; }), title: "Last Year", tabIndex: isHidden ? -1 : 0 } ) }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "calendar-input__year", children: calendarMonthDate.getFullYear() }), /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx( IconButton, { className: "icon-button--small1x icon-button--borderless", icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "utds-icon-before-double-arrow-right", "aria-hidden": "true" }), isDisabled, onClick: () => setCurrentValueDateInternal((draftDate) => { const newDate = dateFns.add(draftDate && dateFns.isValid(draftDate) ? draftDate : /* @__PURE__ */ new Date(), { years: 1 }); addPoliteMessage(`Year has changed to ${newDate.getFullYear()}`); return newDate; }), title: "Next Year", tabIndex: isHidden ? -1 : 0 } ) }) ] }) ] }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-input__grid", id, children: [ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "calendar-input__row", role: "row", children: [ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "calendar-input__cell-header", role: "gridcell", children: "Su" }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: "calendar-input__cell-header", role: "gridcell", children: "Mo" }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: "calendar-input__cell-