@yamada-ui/react
Version:
React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion
243 lines (239 loc) • 7.65 kB
JavaScript
"use client";
import { createContext as createContext$1 } from "../../utils/context.js";
import { runKeyAction, visuallyHiddenAttributes } from "../../utils/dom.js";
import { mergeRefs } from "../../utils/ref.js";
import { utils_exports } from "../../utils/index.js";
import { useControllableState } from "../../hooks/use-controllable-state/index.js";
import { useI18n } from "../../providers/i18n-provider/i18n-provider.js";
import { useFieldProps } from "../field/use-field-props.js";
import { useCombobox, useComboboxItem } from "../../hooks/use-combobox/index.js";
import { cloneElement, isValidElement, useCallback, useMemo, useRef } from "react";
import { jsxs } from "react/jsx-runtime";
//#region src/components/select/use-select.tsx
const defaultRender = ({ count, index, label, separator, value }) => {
const last = count - 1 === index;
return /* @__PURE__ */ jsxs("span", {
style: { marginInlineEnd: "var(--gap)" },
"data-placeholder": (0, utils_exports.dataAttr)(value === ""),
children: [label, !last ? separator : null]
});
};
const [SelectContext, useSelectContext] = createContext$1({ name: "SelectContext" });
const useSelect = (props = {}) => {
const { t } = useI18n("select");
const { context: { labelId } = {}, props: { id, name, multiple = false, closeOnSelect = !multiple, defaultValue = multiple ? [] : "", disabled, focusOnClear = true, includePlaceholder = !multiple, items: itemProp = [], max, placeholder, readOnly, render = defaultRender, required, separator = ",", value: valueProp, onChange: onChangeProp,...rest }, ariaProps, dataProps, eventProps } = useFieldProps(props);
const fieldRef = useRef(null);
const [value, setValue] = useControllableState({
defaultValue,
value: valueProp,
onChange: onChangeProp
});
const onChange = useCallback((value$1) => {
setValue((prev) => {
if ((0, utils_exports.isArray)(prev)) if (prev.includes(value$1)) return prev.filter((prevValue) => prevValue !== value$1);
else if (!(0, utils_exports.isNumber)(max) || prev.length < max) return [...prev, value$1];
else return prev;
else return value$1;
});
}, [max, setValue]);
const items = useMemo(() => {
const items$1 = [...itemProp];
if (placeholder) items$1.unshift({
hidden: !includePlaceholder,
label: placeholder,
value: ""
});
return items$1;
}, [
itemProp,
placeholder,
includePlaceholder
]);
const empty = useMemo(() => !items.filter(({ hidden }) => !hidden).length, [items]);
const { descendants, interactive, open, getContentProps: getComboboxContentProps, getSeparatorProps, getTriggerProps, popoverProps, onActiveDescendant, onClose, onOpen, onSelect } = useCombobox({
closeOnSelect,
disabled,
initialFocusValue: (0, utils_exports.isArray)(value) ? value[0] : value,
readOnly,
onChange,
...ariaProps,
...dataProps,
...eventProps,
...rest
});
const valueMap = useMemo(() => {
const valueMap$1 = {};
items.forEach((item) => {
if ("items" in item) item.items.forEach((item$1) => {
item$1.value ??= (0, utils_exports.isString)(item$1.label) ? item$1.label : void 0;
if (!(0, utils_exports.isUndefined)(item$1.value)) valueMap$1[item$1.value] = item$1;
});
else {
item.value ??= (0, utils_exports.isString)(item.label) ? item.label : void 0;
if (!(0, utils_exports.isUndefined)(item.value)) valueMap$1[item.value] = item;
}
});
return valueMap$1;
}, [items]);
const selectedItems = useMemo(() => {
if ((0, utils_exports.isArray)(value)) if (value.length) return (0, utils_exports.toArray)(value.map((value$1) => valueMap[value$1]));
else return placeholder ? [{
label: placeholder,
value: ""
}] : [];
else return (0, utils_exports.isString)(value) ? (0, utils_exports.toArray)(valueMap[value]) : [];
}, [
placeholder,
value,
valueMap
]);
const children = useMemo(() => {
const count = selectedItems.length;
return selectedItems.map((item, index) => {
const onClear$1 = (ev) => {
ev?.preventDefault();
ev?.stopPropagation();
if (item.value) onChange(item.value);
};
const component = render({
count,
index,
separator,
onClear: onClear$1,
...item
});
if (isValidElement(component)) return cloneElement(component, {
...component.props,
key: index
});
else return component;
});
}, [
onChange,
render,
selectedItems,
separator
]);
const onFocus = useCallback(() => {
if (!interactive || !fieldRef.current) return;
fieldRef.current.focus();
}, [interactive]);
const onClear = useCallback(() => {
if (!interactive) return;
setValue((prev) => (0, utils_exports.isArray)(prev) ? [] : "");
if (!fieldRef.current) return;
if (focusOnClear) fieldRef.current.focus();
}, [
focusOnClear,
interactive,
setValue
]);
const getRootProps = useCallback((props$1) => ({
...dataProps,
...props$1
}), [dataProps]);
const getFieldProps = useCallback(({ ref, "aria-labelledby": ariaLabelledby,...props$1 } = {}) => getTriggerProps({
ref: mergeRefs(ref, fieldRef),
"aria-label": placeholder,
"aria-labelledby": (0, utils_exports.cx)(ariaLabelledby, labelId),
...props$1,
children
}), [
children,
getTriggerProps,
labelId,
placeholder
]);
const getInputProps = useCallback((props$1 = {}) => ({
...dataProps,
...visuallyHiddenAttributes,
id,
name,
defaultValue: (0, utils_exports.isString)(value) ? value : value.join(", "),
disabled,
readOnly,
required,
...props$1,
onFocus: (0, utils_exports.handlerAll)(props$1.onFocus, onFocus)
}), [
dataProps,
disabled,
id,
name,
onFocus,
readOnly,
required,
value
]);
const getContentProps = useCallback((props$1) => getComboboxContentProps({
hidden: empty,
...props$1
}), [empty, getComboboxContentProps]);
const getIconProps = useCallback((props$1) => ({
...dataProps,
...props$1
}), [dataProps]);
return {
descendants,
includePlaceholder,
interactive,
items,
max,
open,
placeholder,
setValue,
value,
valueMap,
getClearIconProps: useCallback((props$1 = {}) => getIconProps({
"aria-disabled": (0, utils_exports.ariaAttr)(!interactive),
"aria-label": t("Clear value"),
role: "button",
tabIndex: interactive ? 0 : -1,
...props$1,
onClick: (0, utils_exports.handlerAll)(props$1.onClick, onClear),
onKeyDown: (0, utils_exports.handlerAll)(props$1.onKeyDown, (ev) => runKeyAction(ev, {
Enter: onClear,
Space: onClear
}))
}), [
getIconProps,
interactive,
onClear,
t
]),
getContentProps,
getFieldProps,
getIconProps,
getInputProps,
getRootProps,
getSeparatorProps,
popoverProps,
onActiveDescendant,
onChange,
onClose,
onOpen,
onSelect
};
};
const useSelectOption = ({ children, closeOnSelect, disabled, hidden, value,...rest } = {}) => {
const { max, value: selectedValue } = useSelectContext();
value ??= (0, utils_exports.isString)(children) ? children : void 0;
const selected = (0, utils_exports.isArray)(selectedValue) ? !(0, utils_exports.isUndefined)(value) && selectedValue.includes(value) : selectedValue === value;
const completed = (0, utils_exports.isNumber)(max) && (0, utils_exports.isArray)(selectedValue) && selectedValue.length >= max;
const { getIndicatorProps, getItemProps } = useComboboxItem({
children,
closeOnSelect,
disabled: disabled || hidden || completed && !selected,
hidden,
selected,
value,
...rest
});
return {
getIndicatorProps,
getOptionProps: useCallback((props = {}) => getItemProps(props), [getItemProps])
};
};
//#endregion
export { SelectContext, useSelect, useSelectContext, useSelectOption };
//# sourceMappingURL=use-select.js.map