UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

167 lines (166 loc) • 6.48 kB
"use client"; import { useTranslation } from "../../i18n/useTranslation.mjs"; import form_default from "../../i18n/resources/en/form.mjs"; import { fieldStyles, fieldVariants } from "../Form/style.mjs"; import FormDivider from "../Form/components/FormDivider.mjs"; import FormTitle from "../Form/components/FormTitle.mjs"; import { resolveBinding } from "./binding.mjs"; import { useFormKitContext } from "./context.mjs"; import { toFieldValidator } from "./schema.mjs"; import { getInternals } from "./instance.mjs"; import { fieldKitStyles } from "./style.mjs"; import { useStoreSelector } from "./useStoreSelector.mjs"; import { cloneElement, isValidElement, memo, useCallback, useEffect, useId, useRef } from "react"; import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime"; import { cx, useResponsive } from "antd-style"; //#region src/base-ui/FormKit/Field.tsx const isEmpty = (value) => value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0; const FieldControl = memo(({ bare, children, deps, emptyValue, getValue, id, name, render, required, trigger, validate, validateDebounce, validateOn, valueProp }) => { const { form } = useFormKitContext(); const { engine, validateOn: formValidateOn } = getInternals(form); const { t } = useTranslation(form_default); const errorId = `${id}-error`; const requiredMessage = typeof required === "string" ? required : t("form.required"); const fieldValidator = validate ? toFieldValidator(validate) : void 0; const validatorRef = useRef(void 0); validatorRef.current = async (value, values) => { if (required && isEmpty(value)) return requiredMessage; return fieldValidator?.(value, values); }; const hasValidator = Boolean(required || validate); const depsKey = deps?.join("\0"); useEffect(() => engine.registerField(name, { debounceMs: validateDebounce, deps: depsKey ? depsKey.split("\0") : void 0, validate: hasValidator ? (value, values) => validatorRef.current(value, values) : void 0, validateOn: validateOn ?? formValidateOn }), [ engine, name, hasValidator, depsKey, validateDebounce, validateOn, formValidateOn ]); const field = useStoreSelector(engine, (e) => e.getField(name)); const child = isValidElement(children) ? children : void 0; const binding = child ? resolveBinding(child, { emptyValue, getValue, trigger, valueProp }) : void 0; const handlerRef = useRef({ binding, childProps: child?.props }); handlerRef.current = { binding, childProps: child?.props }; const onChange = useCallback((...args) => { const { binding: current, childProps } = handlerRef.current; if (current) childProps?.[current.trigger]?.(...args); const next = current ? current.getValue(...args) : args[0]; engine.setValue(name, next, "user"); }, [engine, name]); const onBlur = useCallback((...args) => { handlerRef.current.childProps?.onBlur?.(...args); engine.blurField(name); }, [engine, name]); const warnedRef = useRef(false); useEffect(() => { if (render || child || warnedRef.current || process.env.NODE_ENV === "production") return; warnedRef.current = true; console.warn(`[@lobehub/ui/base-ui/form] Form.Field "${name}" needs a single element child or a render prop; nothing was bound.`); }, [ render, child, name ]); const errorNode = field.error && !bare ? /* @__PURE__ */ jsx("div", { className: fieldStyles.error, id: errorId, children: field.error }) : null; if (render) { const renderProps = { error: field.error, name, onBlur, onChange, touched: field.touched, validating: field.validating, value: field.value }; return /* @__PURE__ */ jsxs(Fragment$1, { children: [render(renderProps), errorNode] }); } if (!child || !binding) return /* @__PURE__ */ jsx(Fragment$1, { children }); const control = cloneElement(child, { "aria-describedby": field.error ? errorId : void 0, "aria-invalid": field.error ? true : void 0, "id": child.props.id ?? id, [binding.trigger]: onChange, [binding.valueProp]: field.value === void 0 ? binding.emptyValue : field.value, "onBlur": onBlur }); return /* @__PURE__ */ jsxs(Fragment$1, { children: [control, errorNode] }); }); FieldControl.displayName = "FormFieldControl"; const FormField = memo(({ avatar, bare, children, className, deps, desc, divider, emptyValue, extra, getValue, hidden, label, layout, minWidth, name, render, required, style, tag, trigger, validate, validateDebounce, validateOn, valueProp, variant }) => { const id = useId(); const { mobile } = useResponsive(); const config = useFormKitContext(); if (hidden) return null; const control = !name ? children : /* @__PURE__ */ jsx(FieldControl, { bare, deps, emptyValue, getValue, id, name, render, required, trigger, validate, validateDebounce, validateOn, valueProp, children }); if (bare) return /* @__PURE__ */ jsx(Fragment$1, { children: control }); const labelFor = render ? void 0 : name ? id : isValidElement(children) ? children.props.id : void 0; const mergedLayout = layout || (mobile ? "vertical" : config.layout); const mergedVariant = variant || config.variant; const controlWidth = mergedLayout === "vertical" || minWidth === void 0 || minWidth === "" ? void 0 : typeof minWidth === "number" ? `${minWidth}px` : minWidth; return /* @__PURE__ */ jsxs(Fragment$1, { children: [divider && /* @__PURE__ */ jsx(FormDivider, { visible: mergedVariant !== "borderless" }), /* @__PURE__ */ jsxs("div", { className: cx(fieldVariants({ layout: mergedLayout }), className), style, children: [/* @__PURE__ */ jsx("label", { className: fieldStyles.label, htmlFor: labelFor, children: /* @__PURE__ */ jsx(FormTitle, { avatar, desc, tag, title: required ? /* @__PURE__ */ jsxs(Fragment$1, { children: [label, /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: fieldKitStyles.required, children: "*" })] }) : label }) }), /* @__PURE__ */ jsxs("div", { style: controlWidth ? { width: controlWidth } : void 0, className: cx(fieldStyles.control, mergedLayout === "vertical" && fieldStyles.controlVertical, controlWidth && fieldKitStyles.controlFixed), children: [control, extra && /* @__PURE__ */ jsx("div", { className: fieldKitStyles.extra, children: extra })] })] })] }); }); FormField.displayName = "FormField"; //#endregion export { FormField as default }; //# sourceMappingURL=Field.mjs.map