@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
391 lines (386 loc) • 13.4 kB
JavaScript
import { FormControlLabel } from './7ZHN3PYD.js';
import { createFormResetListener } from './ANN3A2QM.js';
import { FormControlErrorMessage } from './ICNSTULC.js';
import { FormControlDescription, useFormControlContext, FORM_CONTROL_PROP_NAMES, createFormControl, FormControlContext } from './YKGT7A57.js';
import { createRegisterId } from './E4R2EMM4.js';
import { createControllableSignal } from './BLN63FDC.js';
import { Polymorphic } from './6Y7B2NEO.js';
import { __export } from './5ZKAE4VZ.js';
import { createComponent, mergeProps } from 'solid-js/web';
import { mergeDefaultProps, createGenerateId, mergeRefs, visuallyHiddenStyles, access, callHandler, EventKey } from '@kobalte/utils';
import { createContext, useContext, createUniqueId, splitProps, createSignal, createMemo, createEffect, onCleanup, Show, on } from 'solid-js';
import createPresence from 'solid-presence';
import { combineStyle } from '@solid-primitives/props';
// src/radio-group/index.tsx
var radio_group_exports = {};
__export(radio_group_exports, {
Description: () => FormControlDescription,
ErrorMessage: () => FormControlErrorMessage,
Item: () => RadioGroupItem,
ItemControl: () => RadioGroupItemControl,
ItemDescription: () => RadioGroupItemDescription,
ItemIndicator: () => RadioGroupItemIndicator,
ItemInput: () => RadioGroupItemInput,
ItemLabel: () => RadioGroupItemLabel,
Label: () => RadioGroupLabel,
RadioGroup: () => RadioGroup,
Root: () => RadioGroupRoot,
useRadioGroupContext: () => useRadioGroupContext
});
var RadioGroupContext = createContext();
function useRadioGroupContext() {
const context = useContext(RadioGroupContext);
if (context === void 0) {
throw new Error("[kobalte]: `useRadioGroupContext` must be used within a `RadioGroup` component");
}
return context;
}
var RadioGroupItemContext = createContext();
function useRadioGroupItemContext() {
const context = useContext(RadioGroupItemContext);
if (context === void 0) {
throw new Error("[kobalte]: `useRadioGroupItemContext` must be used within a `RadioGroup.Item` component");
}
return context;
}
// src/radio-group/radio-group-item.tsx
function RadioGroupItem(props) {
const formControlContext = useFormControlContext();
const radioGroupContext = useRadioGroupContext();
const defaultId = `${formControlContext.generateId("item")}-${createUniqueId()}`;
const mergedProps = mergeDefaultProps({
id: defaultId
}, props);
const [local, others] = splitProps(mergedProps, ["value", "disabled", "onPointerDown"]);
const [inputId, setInputId] = createSignal();
const [labelId, setLabelId] = createSignal();
const [descriptionId, setDescriptionId] = createSignal();
const [inputRef, setInputRef] = createSignal();
const [isFocused, setIsFocused] = createSignal(false);
const isDefault = createMemo(() => {
return radioGroupContext.isDefaultValue(local.value);
});
const isSelected = createMemo(() => {
return radioGroupContext.isSelectedValue(local.value);
});
const isDisabled = createMemo(() => {
return local.disabled || formControlContext.isDisabled() || false;
});
const onPointerDown = (e) => {
callHandler(e, local.onPointerDown);
if (isFocused()) {
e.preventDefault();
}
};
const dataset = createMemo(() => ({
...formControlContext.dataset(),
"data-disabled": isDisabled() ? "" : void 0,
"data-checked": isSelected() ? "" : void 0
}));
const context = {
value: () => local.value,
dataset,
isDefault,
isSelected,
isDisabled,
inputId,
labelId,
descriptionId,
inputRef,
select: () => radioGroupContext.setSelectedValue(local.value),
generateId: createGenerateId(() => others.id),
registerInput: createRegisterId(setInputId),
registerLabel: createRegisterId(setLabelId),
registerDescription: createRegisterId(setDescriptionId),
setIsFocused,
setInputRef
};
return createComponent(RadioGroupItemContext.Provider, {
value: context,
get children() {
return createComponent(Polymorphic, mergeProps({
as: "div",
role: "group",
onPointerDown
}, dataset, others));
}
});
}
function RadioGroupItemControl(props) {
const context = useRadioGroupItemContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("control")
}, props);
const [local, others] = splitProps(mergedProps, ["onClick", "onKeyDown"]);
const onClick = (e) => {
callHandler(e, local.onClick);
context.select();
context.inputRef()?.focus();
};
const onKeyDown = (e) => {
callHandler(e, local.onKeyDown);
if (e.key === EventKey.Space) {
context.select();
context.inputRef()?.focus();
}
};
return createComponent(Polymorphic, mergeProps({
as: "div",
onClick,
onKeyDown
}, () => context.dataset(), others));
}
function RadioGroupItemDescription(props) {
const context = useRadioGroupItemContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("description")
}, props);
createEffect(() => onCleanup(context.registerDescription(mergedProps.id)));
return createComponent(Polymorphic, mergeProps({
as: "div"
}, () => context.dataset(), mergedProps));
}
function RadioGroupItemIndicator(props) {
const context = useRadioGroupItemContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("indicator")
}, props);
const [local, others] = splitProps(mergedProps, ["ref", "forceMount"]);
const [ref, setRef] = createSignal();
const {
present
} = createPresence({
show: () => local.forceMount || context.isSelected(),
element: () => ref() ?? null
});
return createComponent(Show, {
get when() {
return present();
},
get children() {
return createComponent(Polymorphic, mergeProps({
as: "div",
ref(r$) {
const _ref$ = mergeRefs(setRef, local.ref);
typeof _ref$ === "function" && _ref$(r$);
}
}, () => context.dataset(), others));
}
});
}
function RadioGroupItemInput(props) {
const formControlContext = useFormControlContext();
const radioGroupContext = useRadioGroupContext();
const radioContext = useRadioGroupItemContext();
const mergedProps = mergeDefaultProps({
id: radioContext.generateId("input")
}, props);
const [local, others] = splitProps(mergedProps, ["ref", "style", "aria-labelledby", "aria-describedby", "onChange", "onFocus", "onBlur"]);
const ariaLabelledBy = () => {
return [
local["aria-labelledby"],
radioContext.labelId(),
// If there is both an aria-label and aria-labelledby, add the input itself has an aria-labelledby
local["aria-labelledby"] != null && others["aria-label"] != null ? others.id : void 0
].filter(Boolean).join(" ") || void 0;
};
const ariaDescribedBy = () => {
return [local["aria-describedby"], radioContext.descriptionId(), radioGroupContext.ariaDescribedBy()].filter(Boolean).join(" ") || void 0;
};
const [isInternalChangeEvent, setIsInternalChangeEvent] = createSignal(false);
const onChange = (e) => {
callHandler(e, local.onChange);
e.stopPropagation();
if (!isInternalChangeEvent()) {
radioGroupContext.setSelectedValue(radioContext.value());
const target = e.target;
target.checked = radioContext.isSelected();
}
setIsInternalChangeEvent(false);
};
const onFocus = (e) => {
callHandler(e, local.onFocus);
radioContext.setIsFocused(true);
};
const onBlur = (e) => {
callHandler(e, local.onBlur);
radioContext.setIsFocused(false);
};
createEffect(on([() => radioContext.isSelected(), () => radioContext.value()], (c) => {
if (!c[0] && c[1] === radioContext.value())
return;
setIsInternalChangeEvent(true);
const ref = radioContext.inputRef();
ref?.dispatchEvent(new Event("input", {
bubbles: true,
cancelable: true
}));
ref?.dispatchEvent(new Event("change", {
bubbles: true,
cancelable: true
}));
}, {
defer: true
}));
createEffect(() => onCleanup(radioContext.registerInput(others.id)));
return createComponent(Polymorphic, mergeProps({
as: "input",
ref(r$) {
const _ref$ = mergeRefs(radioContext.setInputRef, local.ref);
typeof _ref$ === "function" && _ref$(r$);
},
type: "radio",
get name() {
return formControlContext.name();
},
get value() {
return radioContext.value();
},
get checked() {
return radioContext.isSelected();
},
get required() {
return formControlContext.isRequired();
},
get disabled() {
return radioContext.isDisabled();
},
get readonly() {
return formControlContext.isReadOnly();
},
get style() {
return combineStyle({
...visuallyHiddenStyles
}, local.style);
},
get ["aria-labelledby"]() {
return ariaLabelledBy();
},
get ["aria-describedby"]() {
return ariaDescribedBy();
},
onChange,
onFocus,
onBlur
}, () => radioContext.dataset(), others));
}
function RadioGroupItemLabel(props) {
const context = useRadioGroupItemContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("label")
}, props);
createEffect(() => onCleanup(context.registerLabel(mergedProps.id)));
return createComponent(Polymorphic, mergeProps({
as: "label",
get ["for"]() {
return context.inputId();
}
}, () => context.dataset(), mergedProps));
}
function RadioGroupLabel(props) {
return createComponent(FormControlLabel, mergeProps({
as: "span"
}, props));
}
function RadioGroupRoot(props) {
let ref;
const defaultId = `radiogroup-${createUniqueId()}`;
const mergedProps = mergeDefaultProps({
id: defaultId,
orientation: "vertical"
}, props);
const [local, formControlProps, others] = splitProps(mergedProps, ["ref", "value", "defaultValue", "onChange", "orientation", "aria-labelledby", "aria-describedby"], FORM_CONTROL_PROP_NAMES);
const [selected, setSelected] = createControllableSignal({
value: () => local.value,
defaultValue: () => local.defaultValue,
onChange: (value) => local.onChange?.(value)
});
const {
formControlContext
} = createFormControl(formControlProps);
createFormResetListener(() => ref, () => setSelected(local.defaultValue ?? ""));
const ariaLabelledBy = () => {
return formControlContext.getAriaLabelledBy(access(formControlProps.id), others["aria-label"], local["aria-labelledby"]);
};
const ariaDescribedBy = () => {
return formControlContext.getAriaDescribedBy(local["aria-describedby"]);
};
const isDefaultValue = (value) => {
return value === props.defaultValue;
};
const isSelectedValue = (value) => {
return value === selected();
};
const setSelectedValue = (value) => {
if (formControlContext.isReadOnly() || formControlContext.isDisabled()) {
return;
}
setSelected(value);
if (ref)
for (const el of ref.querySelectorAll("[type='radio']")) {
const radio = el;
radio.checked = isSelectedValue(radio.value);
}
};
const context = {
ariaDescribedBy,
isDefaultValue,
isSelectedValue,
setSelectedValue
};
return createComponent(FormControlContext.Provider, {
value: formControlContext,
get children() {
return createComponent(RadioGroupContext.Provider, {
value: context,
get children() {
return createComponent(Polymorphic, mergeProps({
as: "div",
ref(r$) {
const _ref$ = mergeRefs((el) => ref = el, local.ref);
typeof _ref$ === "function" && _ref$(r$);
},
role: "radiogroup",
get id() {
return access(formControlProps.id);
},
get ["aria-invalid"]() {
return formControlContext.validationState() === "invalid" || void 0;
},
get ["aria-required"]() {
return formControlContext.isRequired() || void 0;
},
get ["aria-disabled"]() {
return formControlContext.isDisabled() || void 0;
},
get ["aria-readonly"]() {
return formControlContext.isReadOnly() || void 0;
},
get ["aria-orientation"]() {
return local.orientation;
},
get ["aria-labelledby"]() {
return ariaLabelledBy();
},
get ["aria-describedby"]() {
return ariaDescribedBy();
}
}, () => formControlContext.dataset(), others));
}
});
}
});
}
// src/radio-group/index.tsx
var RadioGroup = Object.assign(RadioGroupRoot, {
Description: FormControlDescription,
ErrorMessage: FormControlErrorMessage,
Item: RadioGroupItem,
ItemControl: RadioGroupItemControl,
ItemDescription: RadioGroupItemDescription,
ItemIndicator: RadioGroupItemIndicator,
ItemInput: RadioGroupItemInput,
ItemLabel: RadioGroupItemLabel,
Label: RadioGroupLabel
});
export { RadioGroup, RadioGroupItem, RadioGroupItemControl, RadioGroupItemDescription, RadioGroupItemIndicator, RadioGroupItemInput, RadioGroupItemLabel, RadioGroupLabel, RadioGroupRoot, radio_group_exports, useRadioGroupContext, useRadioGroupItemContext };