UNPKG

@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
"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