@yamada-ui/react
Version:
React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion
417 lines (413 loc) • 15.1 kB
JavaScript
"use client";
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
const require_context = require('../../utils/context.cjs');
const require_dom = require('../../utils/dom.cjs');
const require_effect = require('../../utils/effect.cjs');
const require_ref = require('../../utils/ref.cjs');
const require_utils_index = require('../../utils/index.cjs');
const require_hooks_use_controllable_state_index = require('../../hooks/use-controllable-state/index.cjs');
const require_i18n_provider = require('../../providers/i18n-provider/i18n-provider.cjs');
const require_use_field_props = require('../field/use-field-props.cjs');
const require_hooks_use_combobox_index = require('../../hooks/use-combobox/index.cjs');
let react = require("react");
react = require_rolldown_runtime.__toESM(react);
let react_jsx_runtime = require("react/jsx-runtime");
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
//#region src/components/autocomplete/use-autocomplete.tsx
const defaultRender = ({ count, focused, index, label, max, separator }) => {
const last = count - 1 === index;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
style: { marginInlineEnd: "var(--gap)" },
children: [label, (!(0, require_utils_index.utils_exports.isNumber)(max) || count < max) && focused || !last ? separator : null]
});
};
const getInputValue = (item) => (0, require_utils_index.utils_exports.isString)(item?.label) ? item.label : item?.query ?? "";
const defaultFilter = (inputValue, items, matcher) => {
if (!inputValue.length) return items;
return items.map((item) => {
if ("items" in item) {
const items$1 = item.items.filter((item$1) => {
if ("query" in item$1) return matcher(inputValue, item$1.query);
else if ((0, require_utils_index.utils_exports.isString)(item$1.label)) return matcher(inputValue, item$1.label);
});
if (items$1.length) return {
...item,
items: items$1
};
} else if ("query" in item) {
if (matcher(inputValue, item.query)) return item;
} else if ((0, require_utils_index.utils_exports.isString)(item.label)) {
if (matcher(inputValue, item.label)) return item;
}
}).filter(Boolean);
};
const defaultMatcher = (input, target) => target?.toLowerCase().includes(input.toLowerCase()) ?? false;
const [AutocompleteContext, useAutocompleteContext] = require_context.createContext({ name: "AutocompleteContext" });
const useAutocomplete = (props = {}) => {
const { t } = require_i18n_provider.useI18n("autocomplete");
const { props: { id, ref, name, allowCustomValue = false, closeOnChange = false, multiple = false, closeOnSelect = !multiple, defaultInputValue, defaultValue = multiple ? [] : "", disabled, emptyMessage = t("No results found"), filter = defaultFilter, focusOnClear = true, inputValue: inputValueProp, items = [], matcher = defaultMatcher, max, openOnChange = true, openOnClick = true, openOnFocus = true, placeholder, readOnly, render = defaultRender, required, separator = ",", value: valueProp, onChange: onChangeProp, onInputChange: onInputChangeProp,...rest }, ariaProps, dataProps, eventProps } = require_use_field_props.useFieldProps(props);
const fieldRef = (0, react.useRef)(null);
const contentRef = (0, react.useRef)(null);
const inputRef = (0, react.useRef)(null);
const focusByClickRef = (0, react.useRef)(false);
const valueMap = (0, react.useMemo)(() => {
const valueMap$1 = {};
items.forEach((item) => {
if ("items" in item) item.items.forEach((item$1) => {
item$1.value ??= (0, require_utils_index.utils_exports.isString)(item$1.label) ? item$1.label : void 0;
if (!(0, require_utils_index.utils_exports.isUndefined)(item$1.value)) valueMap$1[item$1.value] = item$1;
});
else {
item.value ??= (0, require_utils_index.utils_exports.isString)(item.label) ? item.label : void 0;
if (!(0, require_utils_index.utils_exports.isUndefined)(item.value)) valueMap$1[item.value] = item;
}
});
return valueMap$1;
}, [items]);
const [focused, setFocused] = (0, react.useState)(false);
const [value, setValue] = require_hooks_use_controllable_state_index.useControllableState({
defaultValue,
value: valueProp,
onChange: onChangeProp
});
const [inputValue, setInputValue] = require_hooks_use_controllable_state_index.useControllableState({
defaultValue: defaultInputValue ?? getInputValue((0, require_utils_index.utils_exports.isArray)(value) ? void 0 : valueMap[value]),
value: inputValueProp,
onChange: onInputChangeProp
});
const onChange = (0, react.useCallback)((selectedValue) => {
setValue((prev) => {
if ((0, require_utils_index.utils_exports.isArray)(prev)) if (prev.includes(selectedValue)) return prev.filter((prevValue) => prevValue !== selectedValue);
else if (!(0, require_utils_index.utils_exports.isNumber)(max) || prev.length < max) return [...prev, selectedValue];
else return prev;
else return selectedValue;
});
if ((0, require_utils_index.utils_exports.isArray)(value)) setInputValue("");
else {
const item = valueMap[selectedValue];
setInputValue(getInputValue(item));
}
}, [
max,
setInputValue,
setValue,
value,
valueMap
]);
const { activeDescendant, descendants, interactive, open, getContentProps: getComboboxContentProps, getSeparatorProps, getTriggerProps, popoverProps, onActiveDescendant, onClose, onOpen, onOpenWithActiveDescendant, onSelect } = require_hooks_use_combobox_index.useCombobox({
closeOnSelect,
disabled,
initialFocusValue: (0, require_utils_index.utils_exports.isArray)(value) ? value[0] : value,
openOnClick: false,
openOnEnter: false,
openOnSpace: false,
readOnly,
selectFocusRef: inputRef,
selectOnSpace: false,
onChange,
...ariaProps,
...dataProps,
...eventProps,
...rest
});
const filteredItems = (0, react.useMemo)(() => {
if (!items.length) return [];
return filter(inputValue, items, matcher);
}, [
filter,
inputValue,
items,
matcher
]);
const resolvedItems = (0, react.useMemo)(() => {
return filteredItems.length ? filteredItems : [{
"data-empty": "",
label: emptyMessage
}];
}, [filteredItems, emptyMessage]);
const empty = (0, react.useMemo)(() => !resolvedItems.filter(({ hidden }) => !hidden).length, [resolvedItems]);
const children = (0, react.useMemo)(() => {
if (!(0, require_utils_index.utils_exports.isArray)(value)) return null;
const count = value.length;
return value.map((value$1, index) => {
const item = valueMap[value$1] ?? {
label: value$1,
value: value$1
};
const onClear$1 = (ev) => {
ev?.preventDefault();
ev?.stopPropagation();
if (item.value) onChange(item.value);
};
const component = render({
count,
focused,
index,
max,
separator,
onClear: onClear$1,
...item
});
if ((0, react.isValidElement)(component)) return (0, react.cloneElement)(component, {
...component.props,
key: index
});
else return component;
});
}, [
focused,
max,
onChange,
render,
separator,
value,
valueMap
]);
const hasValues = (0, require_utils_index.utils_exports.isArray)(value) && !!value.length;
const onInputChange = (0, react.useCallback)((ev) => {
if ((0, require_utils_index.utils_exports.isArray)(value) && value.length === max) return;
if ((0, require_utils_index.utils_exports.runIfFn)(closeOnChange, ev)) onClose();
else if ((0, require_utils_index.utils_exports.runIfFn)(openOnChange, ev)) onOpen();
activeDescendant.current = null;
const inputValue$1 = ev.target.value;
setInputValue(inputValue$1);
if (inputValue$1.length || (0, require_utils_index.utils_exports.isArray)(value)) return;
setValue("");
}, [
activeDescendant,
closeOnChange,
max,
onClose,
onOpen,
openOnChange,
setInputValue,
setValue,
value
]);
const onKeyDown = (0, react.useCallback)((ev) => {
if (disabled || require_dom.isComposing(ev)) return;
const inputValue$1 = (0, require_utils_index.utils_exports.cast)(ev.target).value;
require_dom.runKeyAction(ev, {
Backspace: (ev$1) => {
if (!(0, require_utils_index.utils_exports.isArray)(value)) return;
if (!!inputValue$1.length) return;
ev$1.preventDefault();
setValue((prev) => prev.slice(0, -1));
},
Enter: (ev$1) => {
if (!open || !inputValue$1.length || activeDescendant.current) return;
const item = filteredItems[0];
if (!item) {
if (!allowCustomValue || !(0, require_utils_index.utils_exports.isArray)(value)) return;
ev$1.preventDefault();
onSelect(inputValue$1);
} else {
ev$1.preventDefault();
if ("items" in item) onSelect(item.items[0]?.value);
else onSelect(item.value);
}
}
}, { preventDefault: false });
}, [
activeDescendant,
allowCustomValue,
disabled,
filteredItems,
onSelect,
open,
setValue,
value
]);
const onClick = (0, react.useCallback)(() => {
if (!interactive) return;
focusByClickRef.current = true;
inputRef.current?.focus();
if (openOnClick) onOpenWithActiveDescendant(descendants.enabledFirstValue);
}, [
descendants,
interactive,
onOpenWithActiveDescendant,
openOnClick
]);
const onMouseDown = (0, react.useCallback)((ev) => {
if (!openOnFocus) return;
ev.preventDefault();
ev.stopPropagation();
}, [openOnFocus]);
const onFocus = (0, react.useCallback)((ev) => {
ev.preventDefault();
ev.stopPropagation();
setFocused(true);
if (openOnFocus && !focusByClickRef.current) onOpenWithActiveDescendant(descendants.enabledFirstValue);
focusByClickRef.current = false;
}, [
openOnFocus,
onOpenWithActiveDescendant,
descendants.enabledFirstValue
]);
const onBlur = (0, react.useCallback)((ev) => {
setFocused(false);
if ((0, require_utils_index.utils_exports.contains)(fieldRef.current, ev.relatedTarget) || (0, require_utils_index.utils_exports.contains)(contentRef.current, ev.relatedTarget)) ev.preventDefault();
else if ((0, require_utils_index.utils_exports.isArray)(value)) setInputValue("");
else if (allowCustomValue) {
if (inputValue) setValue(inputValue);
} else {
const item = valueMap[value];
setInputValue(getInputValue(item));
}
}, [
allowCustomValue,
inputValue,
setInputValue,
setValue,
value,
valueMap
]);
const onClear = (0, react.useCallback)(() => {
if (!interactive) return;
setValue((prev) => (0, require_utils_index.utils_exports.isArray)(prev) ? [] : "");
setInputValue("");
if (focusOnClear) inputRef.current?.focus();
}, [
focusOnClear,
interactive,
setInputValue,
setValue
]);
require_effect.useUpdateEffect(() => {
if ((0, require_utils_index.utils_exports.isArray)(valueProp)) return;
setInputValue(getInputValue(valueProp ? valueMap[valueProp] : void 0));
}, [valueProp]);
const getRootProps = (0, react.useCallback)((props$1) => ({
...dataProps,
...props$1
}), [dataProps]);
const getFieldProps = (0, react.useCallback)(({ ref: ref$1,...props$1 } = {}) => getTriggerProps({
ref: require_ref.mergeRefs(ref$1, fieldRef),
tabIndex: -1,
...props$1,
onClick: (0, require_utils_index.utils_exports.handlerAll)(props$1.onClick, onClick)
}), [getTriggerProps, onClick]);
const getInputProps = (0, react.useCallback)((props$1 = {}) => ({
id,
ref: require_ref.mergeRefs(props$1.ref, ref, inputRef),
name,
style: {
...!focused && (0, require_utils_index.utils_exports.isArray)(value) && !!value.length ? require_dom.visuallyHiddenAttributes.style : {},
...props$1.style
},
"data-max": (0, require_utils_index.utils_exports.dataAttr)((0, require_utils_index.utils_exports.isArray)(value) && (0, require_utils_index.utils_exports.isNumber)(max) && value.length >= max),
autoCapitalize: "off",
autoComplete: "off",
autoCorrect: "off",
disabled,
placeholder: hasValues ? void 0 : placeholder,
readOnly,
required,
spellCheck: false,
value: inputValue,
...dataProps,
...props$1,
onBlur: (0, require_utils_index.utils_exports.handlerAll)(props$1.onBlur, onBlur),
onChange: (0, require_utils_index.utils_exports.handlerAll)(props$1.onChange, onInputChange),
onFocus: (0, require_utils_index.utils_exports.handlerAll)(props$1.onFocus, onFocus),
onKeyDown: (0, require_utils_index.utils_exports.handlerAll)(props$1.onKeyDown, onKeyDown),
onMouseDown: (0, require_utils_index.utils_exports.handlerAll)(props$1.onMouseDown, onMouseDown)
}), [
dataProps,
disabled,
focused,
hasValues,
id,
inputValue,
max,
name,
onBlur,
onFocus,
onInputChange,
onKeyDown,
onMouseDown,
placeholder,
readOnly,
ref,
required,
value
]);
const getContentProps = (0, react.useCallback)(({ ref: ref$1,...props$1 } = {}) => getComboboxContentProps({
ref: require_ref.mergeRefs(ref$1, contentRef),
hidden: empty,
...props$1
}), [empty, getComboboxContentProps]);
const getIconProps = (0, react.useCallback)((props$1) => ({
...dataProps,
...props$1
}), [dataProps]);
return {
children,
descendants,
inputValue,
interactive,
items: resolvedItems,
max,
open,
setInputValue,
setValue,
value,
valueMap,
getClearIconProps: (0, react.useCallback)((props$1 = {}) => getIconProps({
"aria-disabled": (0, require_utils_index.utils_exports.ariaAttr)(!interactive),
"aria-label": t("Clear value"),
role: "button",
tabIndex: interactive ? 0 : -1,
...props$1,
onClick: (0, require_utils_index.utils_exports.handlerAll)(props$1.onClick, onClear),
onKeyDown: (0, require_utils_index.utils_exports.handlerAll)(props$1.onKeyDown, (ev) => require_dom.runKeyAction(ev, {
Enter: onClear,
Space: onClear
}))
}), [
getIconProps,
interactive,
onClear,
t
]),
getContentProps,
getFieldProps,
getIconProps,
getInputProps,
getRootProps,
getSeparatorProps,
popoverProps,
onActiveDescendant,
onChange,
onClose,
onInputChange,
onOpen,
onSelect
};
};
const useAutocompleteOption = ({ children, closeOnSelect, disabled, hidden, value,...rest } = {}) => {
const { max, value: selectedValue } = useAutocompleteContext();
value ??= (0, require_utils_index.utils_exports.isString)(children) ? children : void 0;
const selected = (0, require_utils_index.utils_exports.isArray)(selectedValue) ? !(0, require_utils_index.utils_exports.isUndefined)(value) && selectedValue.includes(value) : selectedValue === value;
const completed = (0, require_utils_index.utils_exports.isNumber)(max) && (0, require_utils_index.utils_exports.isArray)(selectedValue) && selectedValue.length >= max;
const { getIndicatorProps, getItemProps } = require_hooks_use_combobox_index.useComboboxItem({
children,
closeOnSelect,
disabled: disabled || hidden || completed && !selected,
hidden,
selected,
value,
...rest
});
return {
getIndicatorProps,
getOptionProps: (0, react.useCallback)((props = {}) => getItemProps(props), [getItemProps])
};
};
//#endregion
exports.AutocompleteContext = AutocompleteContext;
exports.useAutocomplete = useAutocomplete;
exports.useAutocompleteContext = useAutocompleteContext;
exports.useAutocompleteOption = useAutocompleteOption;
//# sourceMappingURL=use-autocomplete.cjs.map