UNPKG

@lobehub/ui

Version:

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

102 lines (101 loc) • 4.06 kB
"use client"; import ActionIcon from "../ActionIcon/ActionIcon.mjs"; import { useTranslation } from "../../i18n/useTranslation.mjs"; import form_default from "../../i18n/resources/en/form.mjs"; import { useFormKitContext } from "./context.mjs"; import { getInternals } from "./instance.mjs"; import { listStyles } from "./style.mjs"; import { useStoreSelector } from "./useStoreSelector.mjs"; import FormField from "./Field.mjs"; import { cloneElement, memo, useCallback, useRef } from "react"; import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime"; import { PlusIcon, XIcon } from "lucide-react"; //#region src/base-ui/FormKit/List.tsx let rowSeed = 0; const nextRowKey = () => `row-${rowSeed += 1}`; const createItem = (newItem) => typeof newItem === "function" ? newItem() : newItem && typeof newItem === "object" ? structuredClone(newItem) : newItem ?? {}; const labelFor = (column, index) => column.children.props["aria-label"] ?? (typeof column.title === "string" ? `${column.title} ${index + 1}` : void 0); const FormList = memo(({ addText, children, columns, emptyText, name, newItem }) => { const { form } = useFormKitContext(); const { engine } = getInternals(form); const { t } = useTranslation(form_default); const length = useStoreSelector(engine, (e) => { const value = e.getValue(name); return Array.isArray(value) ? value.length : 0; }); const keysRef = useRef([]); const keys = keysRef.current; if (keys.length > length) keys.length = length; while (keys.length < length) keys.push(nextRowKey()); const add = useCallback((item) => { keysRef.current.push(nextRowKey()); engine.pushItem(name, item); }, [engine, name]); const remove = useCallback((index) => { keysRef.current.splice(index, 1); engine.removeItem(name, index); }, [engine, name]); const move = useCallback((from, to) => { const [moved] = keysRef.current.splice(from, 1); keysRef.current.splice(to, 0, moved); engine.moveItem(name, from, to); }, [engine, name]); const fields = keys.map((key, index) => ({ index, key, name: `${name}.${index}` })); if (children || !columns) return /* @__PURE__ */ jsx(Fragment$1, { children: children?.({ add, fields, move, remove }) }); const gridTemplateColumns = `${columns.map((c) => `minmax(0, ${c.flex ?? 1}fr)`).join(" ")} 36px`; const addButton = /* @__PURE__ */ jsxs("button", { className: listStyles.add, type: "button", onClick: () => add(createItem(newItem)), children: [/* @__PURE__ */ jsx(PlusIcon, { size: 14 }), addText ?? t("form.list.add")] }); return /* @__PURE__ */ jsxs("div", { className: listStyles.table, children: [/* @__PURE__ */ jsx("div", { className: listStyles.head, style: { gridTemplateColumns }, children: columns.map((column) => /* @__PURE__ */ jsx("div", { className: listStyles.title, children: column.title }, column.name)) }), fields.length === 0 ? /* @__PURE__ */ jsxs("div", { className: listStyles.empty, children: [/* @__PURE__ */ jsx("span", { children: emptyText ?? t("form.list.empty") }), addButton] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [fields.map((field) => /* @__PURE__ */ jsxs("div", { className: listStyles.row, style: { gridTemplateColumns }, children: [columns.map((column) => /* @__PURE__ */ jsx("div", { className: listStyles.cell, children: /* @__PURE__ */ jsx(FormField, { bare: true, name: `${field.name}.${column.name}`, required: column.required, validate: column.validate, children: cloneElement(column.children, { "aria-label": labelFor(column, field.index) }) }) }, column.name)), /* @__PURE__ */ jsx(ActionIcon, { "aria-label": t("form.list.remove"), icon: XIcon, size: "small", title: t("form.list.remove"), onClick: () => remove(field.index) })] }, field.key)), /* @__PURE__ */ jsx("div", { className: listStyles.foot, children: addButton })] })] }); }); FormList.displayName = "FormList"; //#endregion export { FormList as default }; //# sourceMappingURL=List.mjs.map