@elcrm/form
v0.1.20
Published
React-компоненты полей формы и хук useForm для elCRM: текст, даты, маски, деньги, селект, связка с формой по имени поля (tree-shakeable ESM).
Maintainers
Readme
@elcrm/form
Поля формы для elCRM + хук useForm: одно поле обновляется само, без лишних перерисовок всей формы.
npm · подробные примеры по каждому полю — Template.md
Установка
npm install @elcrm/form
# или
bun add @elcrm/formНужны react и react-dom ≥ 18.
После обновления пакета в приложении:
elcrm update --fix --testЭто подтянет @elcrm/*, переименует устаревшие поля (ProgressField → RangeField и т.д.) и прогонит проверки.
В теме приложения: --popup-shadow (тени селекта / календаря / цвета) и --field-padding / --field-padding-block / --field-padding-inline (отступ капсулы). Старые --field-note-padding* / --control-inner-padding подхватит тот же elcrm update --fix.
Подключение
Стили полей подхватываются сами при импорте компонентов. Темы подключаете вручную:
import { useForm, StringField } from "@elcrm/form";
import "@elcrm/form/light.css"; // или dark.cssЕсли хотите разделить геометрию и цвета, можно подключать токены отдельно:
import "@elcrm/form/tokens.css";
import "@elcrm/form/light.css"; // или dark.cssНа контейнере или на <html> задайте data-theme="light" | "dark" — от этого зависят цвета полей и всплывающих списков (селект, календарь, цвет).
<div data-theme="light">
{/* форма */}
</div>Быстрый старт
import { useForm, StringField, PasswordField } from "@elcrm/form";
import "@elcrm/form/light.css";
function LoginForm() {
const form = useForm({ login: "", password: "" });
return (
<div data-theme="light">
<StringField
name="login"
form={form}
label="Логин"
native
autoComplete="username"
/>
<PasswordField name="password" form={form} label="Пароль" />
<button type="button" onClick={() => console.log(form.getValues())}>
Сохранить
</button>
</div>
);
}useForm
Хранит значения и уведомляет только поле с изменившимся name.
type Values = { login: string; role: number };
const form = useForm<Values>({ login: "", role: 0 });
form.getValue("login");
form.getValues(); // копия объекта
form.onValue({ name: "login", value: "a" }); // записать без перерисовки
form.setValue({ name: "login", value: "a" }); // записать и обновить поле
form.setValues({ login: "b", role: 1 });
form.reset(); // к initialValues
form.subscribe("login", () => { /* … */ }); // → unsubscribe()| Метод | Когда |
| ----- | ----- |
| onValue | Ввод по символам (без bump) |
| setValue | Выбор / blur / нужно обновить UI |
| setValues / reset | Массово; для Date/массивов — новая ссылка |
Общие пропы полей
Почти у всех компонентов (TInput):
| Проп | Назначение |
| ---- | ---------- |
| name + form | Связь с useForm |
| value / onValue / onBlur | Controlled или колбэки; onBlur → { value, name }, не FocusEvent |
| label, placeholder, error | Подпись, плейсхолдер, ошибка |
| disabled, size | "s" | "m" | "l" у видимых *Field. В <Toolbar> без size — размер тулбара (useToolbar, обычно "s") |
| before / after | Аффиксы (иконки, «₽») |
| hidden | Не CSS-hide: не рендерится только если значение пустое |
Что есть в пакете
Текст и ввод
| Компонент | В form | Как пользоваться |
| --------- | -------- | ---------------- |
| StringField | string | По умолчанию contentEditable; для логина/почты — native + autoComplete |
| SearchField | string | Поиск в форме: timeout, suggestions, minimal, hotkey, variant. Хедер без form — <Search> из @elcrm/search (те же --field-*) |
| PasswordField | string | Глаз; generate (true → Az09#); native = не рендерить |
| TextareaField | string | Многострочный текст |
| EmailField | string | Проверка формата на blur (validate) |
| UrlField | string | type="url" |
| PhoneField | digits | Маска страны; тосты — через Form.Init |
| MaskField | digits | format="___-___-___ __" (_ = слот) |
| NumberField | строка "42" | max = лимит символов, не максимум числа |
| PercentField | number | Clamp min/max, аффикс %, decimals |
| MoneyField | number × course | В UI рубли, в form — минорные единицы |
| CardField | digits | Группы 4×4, Luhn, бренд |
Дата, время, выбор
| Компонент | В form | Как пользоваться |
| --------- | -------- | ---------------- |
| DateField | YYYY-MM-DD | "" | Календарь; пустое ≠ «сегодня» |
| TimeField | HH:mm | "" | picker, presets, step |
| SelectField | number (id) | options={{ 1: { n: "Админ", s: 1 } }} — s: 0 скрывает |
| RadioField | id | options + inline |
| TabsField | id | Сегменты; variant="field"|"pill", equal (по умолчанию растягиваются) |
| CheckField | boolean | placeholder — текст рядом с квадратом; variant="field"|"plain" |
| OptionsField / ModalField | зависит от outFormat | Выбор в модалке приложения — нужен Form.Init |
Прочее
| Компонент | В form | Как пользоваться |
| --------- | -------- | ---------------- |
| RangeField | number | variant="bar"|"line", min/max/step |
| TagsField | string[] | Чипы; maxTags, separators |
| RatingField | number | max, allowClear |
| CodeField | string | OTP-ячейки; крупно — cellSize={80} |
| ColorField | hex | Пикер; colors, alpha, inlinePalette (пресеты в поле) |
| FileField / DragDropField | File / мета | accept, multiple, maxSize |
| RichTextField | HTML-строка | toolbar; санитизация — на стороне приложения |
| HiddenField | любое | Без UI |
| DisplayField | любое | Только показ; format, emptyText |
| FieldGroup | через form | Несколько полей одним items={[…]} |
Алиасы вроде ProgressField / OtpField удалены. После обновления:
elcrm update --fix --test
# или только код:
elcrm migrate form-aliasesFieldGroup
Общие form / disabled / size у родителя; у каждого item — field + name + свои пропы:
<FieldGroup
form={form}
gap={12}
items={[
{ field: "string", name: "login", label: "Логин", native: true },
{ field: "email", name: "email", label: "Email" },
{ field: "select", name: "role", label: "Роль", options: ROLES },
]}
/>field: string | password | email | number | select | card | color | … — полный список в типе FieldGroupFieldKind.
Form.Init
Нужен для тостов (копирование телефона) и модалок OptionsField / ModalField. Рендерит null.
<>
<Form.Init
onNotice={(message) => { /* тост */ }}
onModal={({ module, modalName, value, callback }) => {
/* открыть UI → callback(result) */
}}
/>
<PhoneField name="phone" form={form} label="Телефон" />
</>Без Form.Init эти колбэки — no-op.
Частые ошибки
| Ловушка | Как правильно |
| ------- | ------------- |
| hidden | Не CSS-hide: скрывает поле только при пустом значении |
| StringField vs PasswordField native | У String — нативный input; у Password — «не рендерить» |
| Select options | { n, s }, не { value, label } |
| Number vs Percent | Number → строка цифр; Percent → число + clamp |
| onBlur | { value, name }, не DOM-событие |
| options / объекты в пропах | Вынести в константу модуля — не создавать на каждый рендер |
Примеры
Полный каталог сниппетов по каждому полю: Template.md.
Лицензия
MIT © MaSkal
