@veracity/vui
Version:
Veracity UI is a React component library crafted for use within Veracity applications and pages. Based on Styled Components and @xstyled.
182 lines (174 loc) • 5.91 kB
JavaScript
import { filterUndefined } from "../utils/object.js";
import { cs } from "../utils/styles.js";
import { selectionControlColors, selectionControlDisabled } from "../selectionControl/consts.js";
import { omitThemingProps, th, useStyleConfig } from "../core/theme.js";
import styled from "../core/styled.js";
import vui from "../core/vui.js";
import { Box } from "../box/box.js";
import { T } from "../t/t.js";
import { SelectionControlInput, useDeprecatedLgSize, useSelectionControlColors, useSelectionControlDescription } from "../selectionControl/selectionControl.js";
import { useRadioGroupContext } from "./context.js";
import { useEffect, useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
//#region src/radio/radio.tsx
const dotSize = {
lg: "12px",
md: "12px",
sm: "10px",
xs: "8px"
};
const jsxChildrenMt = {
lg: "0px",
md: "0px",
sm: "2px",
xs: "3px"
};
const labelMt = {
lg: "0px",
md: "0px",
sm: "0.5px",
xs: "1px"
};
const RadioControl = styled.spanBox`
align-items: center;
border: 1px solid currentColor;
border-radius: 50%;
display: inline-flex;
flex-shrink: 0;
justify-content: center;
position: relative;
transition-duration: fast;
`;
const RadioDot = styled.spanBox`
background-color: currentColor;
border-radius: 50%;
flex-shrink: 0;
`;
const RadioBase = styled.labelBox`
cursor: pointer;
display: inline-flex;
width: fit-content;
&:not([aria-disabled='true']):not([aria-readonly='true']):hover .vui-radioControl {
color: ${(p) => th.color(p.controlHoverColor)};
}
&:not([aria-disabled='true']):not([aria-readonly='true']):active .vui-radioControl {
color: ${(p) => th.color(p.controlPressedColor)};
}
&[aria-readonly='true'] {
cursor: auto;
}
&:not([aria-disabled='true']):not([aria-readonly='true']) .vui-radioControl:focus-within {
color: ${selectionControlColors.focus};
}
&[aria-disabled='true'] {
color: ${selectionControlDisabled.text};
cursor: not-allowed;
.vui-radioControl {
color: ${selectionControlDisabled.icon};
}
}
`;
/**
* Allows selection of a single choice from a set of items. Handles controlled and uncontrolled modes.
* The ring and the inner dot shown when checked are both plain CSS — no icon assets involved.
*/
const Radio = vui((props, ref) => {
const { defaultValue: groupDefaultValue, isChecked: groupIsChecked, onChange: groupOnChange, value: groupValue, ...radioGroupProps } = useRadioGroupContext() ?? {};
const defaultIsChecked = groupIsChecked !== void 0 ? groupIsChecked === props.value : void 0;
const mergedProps = {
...radioGroupProps,
...props
};
const { checked = groupValue !== void 0 ? props.value === groupValue : void 0, children, className, defaultChecked = groupDefaultValue !== void 0 ? props.value === groupDefaultValue : void 0, disabled, errorText, readOnly, helpText, id: idProp, inputProps, inputRef, label, name, onChange, required, value, ...rest } = omitThemingProps(mergedProps);
const [isChecked, setIsChecked] = useState(defaultIsChecked ?? checked ?? defaultChecked);
const styles = useStyleConfig("Radio", mergedProps);
const { color: controlColor, hoverColor, pressedColor, ...controlStyles } = styles.control;
const size = mergedProps.size ?? "md";
const { description, descriptionId, errorText: effectiveErrorText, id } = useSelectionControlDescription(idProp, helpText, errorText, !!isChecked);
const hasJsxChildren = !!children;
const controlMr = children || label ? 1 : 0;
const { color, controlHoverColor, controlPressedColor, descriptionColor, labelColor } = useSelectionControlColors({
controlColor,
disabled,
errorText: effectiveErrorText,
hoverColor,
isActive: !!isChecked,
pressedColor,
readOnly
});
useDeprecatedLgSize(size, "Radio");
useEffect(() => {
if (groupIsChecked !== void 0) setIsChecked(groupIsChecked === value);
else if (checked !== void 0) setIsChecked(checked);
}, [checked, groupIsChecked]);
function handleOnChange(e) {
if (readOnly) return;
if (checked === void 0 && groupIsChecked === void 0) setIsChecked(e.target.checked);
groupOnChange?.(e);
onChange?.(e);
}
const aliasedProps = filterUndefined({
"aria-disabled": disabled,
"aria-readonly": readOnly
});
return /* @__PURE__ */ jsxs(RadioBase, {
alignItems: label || hasJsxChildren ? "flex-start" : "center",
className: cs("vui-radio", className),
controlHoverColor,
controlPressedColor,
ref,
...styles.container,
...aliasedProps,
...rest,
children: [/* @__PURE__ */ jsxs(RadioControl, {
bg: "white",
className: "vui-radioControl",
color,
focusWithinRing: 3,
mr: controlMr,
mt: hasJsxChildren ? jsxChildrenMt[size] ?? jsxChildrenMt.md : label ? labelMt[size] ?? labelMt.md : void 0,
...controlStyles,
children: [/* @__PURE__ */ jsx(SelectionControlInput, {
"aria-describedby": descriptionId,
"aria-invalid": !!effectiveErrorText || void 0,
className: "vui-radioInput",
onChange: handleOnChange,
ref: inputRef,
type: "radio",
checked,
defaultChecked,
disabled,
id,
name,
required,
value,
...inputProps
}), isChecked && /* @__PURE__ */ jsx(RadioDot, {
className: "vui-radioDot",
h: dotSize[size] ?? dotSize.md,
w: dotSize[size] ?? dotSize.md
})]
}), children ?? (label && /* @__PURE__ */ jsxs(Box, {
column: true,
children: [/* @__PURE__ */ jsx(T, {
className: "vui-radioLabel",
color: labelColor,
variant: "caption",
...styles.label,
children: label
}), description && /* @__PURE__ */ jsx(T, {
className: "vui-radioDescription",
color: descriptionColor,
id: descriptionId,
mt: "2px",
size: "sm",
children: description
})]
}))]
});
});
Radio.displayName = "Radio";
//#endregion
export { Radio, Radio as default, RadioBase };
globalThis.__vuiVersion__ = "5.4.0"
//# sourceMappingURL=radio.js.map