@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
332 lines (331 loc) • 10.4 kB
JavaScript
"use client";
var _AlignmentHelper, _span;
import withComponentMarkers from "../../shared/helpers/withComponentMarkers.js";
import React, { useContext, useRef, useState, useCallback } from 'react';
import clsx from 'clsx';
import useId from "../../shared/helpers/useId.js";
import { extendExistingPropsWithContext, validateDOMAttributes, getStatusState, combineDescribedBy, dispatchCustomElementEvent, removeUndefinedProps } from "../../shared/component-helper.js";
import AlignmentHelper from "../../shared/AlignmentHelper.js";
import { applySpacing } from "../space/SpacingUtils.js";
import { skeletonDOMAttributes, createSkeletonClass } from "../skeleton/SkeletonHelper.js";
import FormLabel from "../form-label/FormLabel.js";
import FormStatus from "../form-status/FormStatus.js";
import RadioGroup from "./RadioGroup.js";
import RadioGroupContext from "./RadioGroupContext.js";
import Context from "../../shared/Context.js";
import Suffix from "../../shared/helpers/Suffix.js";
import { pickFormElementProps } from "../../shared/helpers/filterValidProps.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const radioDefaultProps = {
label: null,
labelSrOnly: null,
labelPosition: null,
checked: null,
disabled: null,
id: null,
size: null,
element: 'input',
group: null,
status: null,
statusState: 'error',
statusProps: null,
statusNoAnimation: null,
globalStatus: null,
suffix: null,
value: '',
readOnly: false,
skeleton: null,
className: null,
children: null,
onChange: null,
ref: null
};
const parseChecked = state => /true|on/.test(String(state));
function RadioInner({
ref: externalRef,
...ownProps
}) {
const groupContext = useContext(RadioGroupContext);
const context = useContext(Context);
const inputRef = useRef(null);
const ownPropsRef = useRef(ownProps);
ownPropsRef.current = ownProps;
const groupContextRef = useRef(groupContext);
groupContextRef.current = groupContext;
const id = useId(ownProps.id);
const [checkedState, setCheckedState] = useState(() => parseChecked(ownProps.checked));
const [prevPropsChecked, setPrevPropsChecked] = useState(ownProps.checked);
const skipNextPropSync = useRef(false);
if (ownProps.checked !== prevPropsChecked) {
setPrevPropsChecked(ownProps.checked);
if (!skipNextPropSync.current) {
setCheckedState(parseChecked(ownProps.checked));
}
}
skipNextPropSync.current = false;
const isContextGroupOrSingle = useCallback(() => typeof groupContext.value !== 'undefined' && !ownProps.group, [groupContext.value, ownProps.group]);
const isPlainGroup = useCallback(() => typeof groupContext.value === 'undefined' && ownProps.group, [groupContext.value, ownProps.group]);
const isInNoGroup = useCallback(() => typeof groupContext.value === 'undefined' && !ownProps.group, [groupContext.value, ownProps.group]);
const callOnChange = useCallback(({
value,
checked: isChecked,
event
}) => {
const {
group
} = ownPropsRef.current;
if (groupContextRef.current.onChange) {
groupContextRef.current.onChange({
value,
event
});
}
dispatchCustomElementEvent({
props: ownPropsRef.current
}, 'onChange', {
group,
checked: isChecked,
value,
event
});
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
const onChangeHandler = useCallback(_event => {
const event = _event;
if (ownPropsRef.current.readOnly) {
return event.preventDefault();
}
const value = event.target.value;
const newChecked = !checkedState;
if (isPlainGroup()) {
setTimeout(() => {
skipNextPropSync.current = true;
setCheckedState(newChecked);
callOnChange({
value,
checked: newChecked,
event
});
}, 1);
} else {
skipNextPropSync.current = true;
setCheckedState(newChecked);
callOnChange({
value,
checked: newChecked,
event
});
}
}, [checkedState, isPlainGroup, callOnChange]);
const onKeyDownHandler = useCallback(event => {
const key = event.key;
if (isInNoGroup()) {
if (key === 'Enter') {
onChangeHandler(event);
}
} else if (isContextGroupOrSingle()) {
if (key === 'Enter' || key === ' ') {
const {
value
} = groupContextRef.current;
if (value !== null && typeof value !== 'undefined') {
event.preventDefault();
}
onChangeHandler(event);
}
} else {
if (key === ' ') {
event.preventDefault();
}
}
dispatchCustomElementEvent({
props: ownPropsRef.current
}, 'onKeyDown', {
event
});
}, [isInNoGroup, isContextGroupOrSingle, onChangeHandler]);
const onClickHandler = useCallback(event => {
if (ownPropsRef.current.readOnly) {
return event.preventDefault();
}
if (!isPlainGroup()) {
return;
}
const value = event.target.value;
const isChecked = event.target.checked;
callOnChange({
value,
checked: isChecked,
event
});
}, [isPlainGroup, callOnChange]);
const resolvedProps = {
...radioDefaultProps,
...removeUndefinedProps({
...ownProps
})
};
const contextProps = extendExistingPropsWithContext(resolvedProps, radioDefaultProps, groupContext);
const props = extendExistingPropsWithContext(resolvedProps, radioDefaultProps, contextProps, {
skeleton: context === null || context === void 0 ? void 0 : context.skeleton
}, pickFormElementProps(context.formElement), context === null || context === void 0 ? void 0 : context.Radio);
const {
status,
statusState,
statusProps,
statusNoAnimation,
globalStatus,
suffix,
element,
label,
labelSrOnly,
labelPosition,
size,
readOnly,
skeleton,
className,
id: _id,
group: _group,
value: _value,
checked: _checked,
disabled: _disabled,
children,
onChange,
ref: _ref,
...rest
} = props;
for (const key of Object.keys(ownProps)) {
if (!(key in radioDefaultProps)) {
;
rest[key] = ownProps[key];
}
}
let checked = checkedState;
const {
value
} = props;
let {
group,
disabled
} = props;
const hasContext = typeof groupContext.name !== 'undefined';
if (hasContext) {
if (typeof groupContext.value !== 'undefined') {
checked = groupContext.value === value;
}
group = groupContext.name;
if (groupContext.disabled && disabled !== false) {
disabled = true;
}
} else if (typeof rest.name !== 'undefined') {
group = rest.name;
}
const showStatus = getStatusState(status);
const mainParams = applySpacing(props, {
className: clsx('dnb-radio', className, status && `dnb-radio__status--${statusState}`, size && `dnb-radio--${size}`, label && `dnb-radio--label-position-${labelPosition || 'right'}`)
});
let inputParams = {
role: hasContext || group ? 'radio' : null,
type: hasContext || group ? 'radio' : 'checkbox'
};
if (!group) {
inputParams.type = 'checkbox';
inputParams.role = 'radio';
}
if (showStatus || suffix) {
inputParams['aria-describedby'] = combineDescribedBy(inputParams, showStatus ? id + '-status' : null, suffix ? id + '-suffix' : null);
}
if (readOnly) {
inputParams['aria-readonly'] = inputParams.readOnly = true;
}
inputParams = Object.assign(inputParams, rest);
skeletonDOMAttributes(inputParams, skeleton, context);
validateDOMAttributes(ownProps, inputParams);
const labelComp = label && _jsx(FormLabel, {
id: id + '-label',
forId: id,
text: label,
disabled: disabled,
skeleton: skeleton,
srOnly: labelSrOnly,
vertical: false
});
const Element = element || 'input';
const combinedRef = useCallback(el => {
;
inputRef.current = el;
if (typeof externalRef === 'function') {
externalRef(el);
} else if (externalRef) {
;
externalRef.current = el;
}
}, [externalRef]);
return _jsx("span", {
...mainParams,
children: _jsxs("span", {
className: "dnb-radio__order",
children: [labelPosition === 'left' && labelComp, _jsxs("span", {
className: "dnb-radio__inner",
children: [_AlignmentHelper || (_AlignmentHelper = _jsx(AlignmentHelper, {})), _jsx(FormStatus, {
show: showStatus,
id: id + '-form-status',
globalStatus: globalStatus,
label: label,
textId: id + '-status',
widthSelector: id + ', ' + id + '-label',
text: status,
state: statusState,
noAnimation: statusNoAnimation,
skeleton: skeleton,
...statusProps
}), _jsxs("span", {
className: "dnb-radio__row",
children: [_jsxs("span", {
className: "dnb-radio__shell",
children: [_jsx(Element, {
type: "radio",
value: value,
id: id,
name: group,
className: "dnb-radio__input",
checked: checked,
"aria-checked": isPlainGroup() ? undefined : checked,
disabled: disabled,
ref: combinedRef,
...inputParams,
onChange: onChangeHandler,
onClick: onClickHandler,
onKeyDown: onKeyDownHandler
}), _jsx("span", {
className: clsx('dnb-radio__button', createSkeletonClass('shape', skeleton, context)),
"aria-hidden": true
}), _span || (_span = _jsx("span", {
className: "dnb-radio__focus",
"aria-hidden": true
})), _jsx("span", {
className: clsx('dnb-radio__dot', createSkeletonClass('font', skeleton, context)),
"aria-hidden": true
})]
}), labelPosition !== 'left' && labelComp, suffix && _jsx(Suffix, {
className: "dnb-radio__suffix",
id: id + '-suffix',
context: props,
children: suffix
})]
})]
})]
})
});
}
const Radio = React.memo(RadioInner);
Radio.Group = RadioGroup;
Radio.parseChecked = parseChecked;
withComponentMarkers(Radio, {
_formElement: true,
_supportsSpacingProps: true
});
export default Radio;
//# sourceMappingURL=Radio.js.map