@utahdts/utah-design-system
Version:
Utah Design System React Library
1,453 lines • 262 kB
JavaScript
(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-