@reformer/ui-kit 5.0.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,9 @@
1
1
  import { ComponentType, ReactNode } from 'react';
2
- import { ArrayNode, FieldPathNode, FormArrayProxy, FormFields, FormProxy } from '@reformer/core';
2
+ import { ArrayNode, FormArrayProxy, FormProxy } from '@reformer/core';
3
3
  import { FieldWrapperProps } from '@reformer/renderer-react';
4
- export interface FormArraySectionProps<T extends FormFields> {
5
- /**
6
- * Резолвится автоматически: уже-резолвленный ArrayNode/FormArrayProxy ИЛИ
7
- * FieldPathNode (path.<arrayField>) — в этом случае через `form` + navigator.
8
- */
9
- control: FormArrayProxy<T> | ArrayNode<T> | FieldPathNode<unknown, unknown> | undefined;
4
+ export interface FormArraySectionProps<T extends object> {
5
+ /** Уже-резолвленный ArrayNode/ModelArrayNode/FormArrayProxy. */
6
+ control: FormArrayProxy<T> | ArrayNode<T> | undefined;
10
7
  /** React FC получает `control: FormProxy<T>` для каждого элемента. */
11
8
  itemComponent: ComponentType<{
12
9
  control: FormProxy<T>;
@@ -39,6 +36,11 @@ export interface FormArraySectionProps<T extends FormFields> {
39
36
  initialValue?: Partial<T>;
40
37
  /** Показывать «Удалить» когда остался один элемент. По умолчанию `false`. */
41
38
  showRemoveOnSingle?: boolean;
39
+ /**
40
+ * Показывать кнопки ↑/↓ для перестановки элементов. По умолчанию `false`
41
+ * (обратная совместимость — существующие массивы не меняются).
42
+ */
43
+ reorderable?: boolean;
42
44
  /** Максимум items — AddButton отключается при достижении. */
43
45
  maxItems?: number;
44
46
  /** Внешний className секции. */
@@ -57,4 +59,4 @@ export interface FormArraySectionProps<T extends FormFields> {
57
59
  */
58
60
  fieldWrapper?: ComponentType<FieldWrapperProps>;
59
61
  }
60
- export declare function FormArraySection<T extends FormFields>({ control, itemComponent: ItemComponent, title, itemLabel, addButtonLabel, removeButtonLabel, emptyMessage, emptyMessageHint, hasItems, initialValue, showRemoveOnSingle, maxItems, className, cardClassName, form, }: FormArraySectionProps<T>): ReactNode;
62
+ export declare function FormArraySection<T extends object>({ control, itemComponent: ItemComponent, title, itemLabel, addButtonLabel, removeButtonLabel, emptyMessage, emptyMessageHint, hasItems, initialValue, showRemoveOnSingle, reorderable, maxItems, className, cardClassName, }: FormArraySectionProps<T>): ReactNode;
@@ -0,0 +1,107 @@
1
+ import { jsx as e, jsxs as r } from "react/jsx-runtime";
2
+ import { useFormControl as _ } from "@reformer/core";
3
+ import { FormArray as o } from "@reformer/cdk/form-array";
4
+ function B(n) {
5
+ return n ? typeof n == "object" && typeof n.push == "function" && typeof n.removeAt == "function" ? n : (typeof console < "u" && console.warn("[FormArraySection] control is not an ArrayNode/FormArrayProxy."), null) : null;
6
+ }
7
+ function E({
8
+ control: n,
9
+ itemComponent: p,
10
+ title: i,
11
+ itemLabel: s,
12
+ addButtonLabel: c = "+ Добавить",
13
+ removeButtonLabel: v = "Удалить",
14
+ emptyMessage: u,
15
+ emptyMessageHint: m,
16
+ hasItems: y,
17
+ initialValue: f,
18
+ showRemoveOnSingle: g = !1,
19
+ reorderable: x = !1,
20
+ maxItems: b,
21
+ className: N = "space-y-3 mt-2",
22
+ cardClassName: w = "mb-4 p-4 bg-white rounded border"
23
+ }) {
24
+ const l = B(n), j = _(
25
+ l ?? void 0
26
+ );
27
+ if (y === !1 || !l) return null;
28
+ const d = j?.length ?? 0, h = b != null && d >= b, A = (a, t) => typeof s == "function" ? s(a, t) : `${s ?? i ?? "Элемент"} #${t + 1}`;
29
+ return /* @__PURE__ */ e(o.Root, { control: l, children: /* @__PURE__ */ r("section", { className: N, children: [
30
+ i ? /* @__PURE__ */ r("div", { className: "flex justify-between items-center mb-4", children: [
31
+ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: i }),
32
+ h ? null : /* @__PURE__ */ e(
33
+ o.AddButton,
34
+ {
35
+ initialValue: f,
36
+ "data-testid": "array-add",
37
+ className: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground shadow hover:bg-primary/90 h-9 px-4 py-2",
38
+ children: c
39
+ }
40
+ )
41
+ ] }) : null,
42
+ /* @__PURE__ */ e(o.List, { className: "space-y-3", children: ({ control: a, index: t, remove: C, moveUp: $, moveDown: k, canMoveUp: F, canMoveDown: R }) => {
43
+ const S = g || d > 1;
44
+ return /* @__PURE__ */ r("div", { className: w, "data-testid": `array-item-${t}`, children: [
45
+ i || s ? /* @__PURE__ */ r("div", { className: "flex justify-between items-center mb-3", children: [
46
+ /* @__PURE__ */ e("h4", { className: "font-medium", children: A(a, t) }),
47
+ /* @__PURE__ */ r("div", { className: "flex items-center gap-2", children: [
48
+ x ? /* @__PURE__ */ r("div", { className: "flex items-center gap-1", children: [
49
+ /* @__PURE__ */ e(
50
+ "button",
51
+ {
52
+ type: "button",
53
+ onClick: $,
54
+ disabled: !F,
55
+ "aria-label": "Переместить вверх",
56
+ "data-testid": `array-item-${t}-move-up`,
57
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground h-9 w-9",
58
+ children: "↑"
59
+ }
60
+ ),
61
+ /* @__PURE__ */ e(
62
+ "button",
63
+ {
64
+ type: "button",
65
+ onClick: k,
66
+ disabled: !R,
67
+ "aria-label": "Переместить вниз",
68
+ "data-testid": `array-item-${t}-move-down`,
69
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground h-9 w-9",
70
+ children: "↓"
71
+ }
72
+ )
73
+ ] }) : null,
74
+ S ? /* @__PURE__ */ e(
75
+ "button",
76
+ {
77
+ type: "button",
78
+ onClick: C,
79
+ "data-testid": `array-item-${t}-remove`,
80
+ className: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-destructive text-destructive-foreground shadow hover:bg-destructive/90 h-9 px-4 py-2",
81
+ children: v
82
+ }
83
+ ) : null
84
+ ] })
85
+ ] }) : null,
86
+ /* @__PURE__ */ e(p, { control: a })
87
+ ] });
88
+ } }),
89
+ d === 0 && u ? /* @__PURE__ */ e(o.Empty, { children: /* @__PURE__ */ r("div", { className: "p-4 bg-gray-100 border border-gray-300 rounded text-center text-gray-600", children: [
90
+ u,
91
+ m && /* @__PURE__ */ e("div", { className: "mt-2 text-xs text-gray-500", children: m })
92
+ ] }) }) : null,
93
+ !i && !h ? /* @__PURE__ */ e("div", { children: /* @__PURE__ */ e(
94
+ o.AddButton,
95
+ {
96
+ initialValue: f,
97
+ "data-testid": "array-add",
98
+ className: "text-sm text-blue-600 hover:text-blue-700 hover:underline",
99
+ children: c
100
+ }
101
+ ) }) : null
102
+ ] }) });
103
+ }
104
+ E.__selfManagedChildren = !0;
105
+ export {
106
+ E as F
107
+ };
@@ -1,4 +1,4 @@
1
- import { F as a } from "./form-array-section-CItAR061.js";
1
+ import { F as a } from "./form-array-section-D1c2AQzE.js";
2
2
  export {
3
3
  a as FormArraySection
4
4
  };
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import { Section as O } from "./ui/section.js";
15
15
  import { Select as X, SelectContent as Y, SelectGroup as _, SelectItem as $, SelectLabel as ee, SelectScrollDownButton as oe, SelectScrollUpButton as te, SelectTrigger as re, SelectValue as ne } from "./ui/select.js";
16
16
  import { Textarea as ie } from "./ui/textarea.js";
17
17
  import { E as ae, L as ce } from "./loading-state-4VeOE6iN.js";
18
- import { F as pe } from "./form-array-section-CItAR061.js";
18
+ import { F as pe } from "./form-array-section-D1c2AQzE.js";
19
19
  import { F as me, a as xe, b as ge, S as ue } from "./form-wizard-C-yRYqTI.js";
20
20
  const f = () => /* @__PURE__ */ o(
21
21
  "svg",
@@ -1,47 +1,47 @@
1
- import { jsx as d, jsxs as E, Fragment as w } from "react/jsx-runtime";
2
- import * as D from "react";
3
- import { forwardRef as f, Children as S, isValidElement as B, cloneElement as L, useId as A, useMemo as x, createContext as V, useContext as W } from "react";
4
- import { useFormControl as M } from "@reformer/core";
5
- import { Checkbox as O } from "./checkbox.js";
6
- function T(r, n) {
7
- const l = { ...r };
8
- for (const e of Object.keys(n)) {
9
- const o = r[e], t = n[e];
10
- e.startsWith("on") && typeof o == "function" && typeof t == "function" ? l[e] = (...i) => {
11
- t(...i), o(...i);
12
- } : e === "className" && typeof o == "string" && typeof t == "string" ? l[e] = [t, o].filter(Boolean).join(" ") : e === "style" && typeof o == "object" && typeof t == "object" ? l[e] = { ...t, ...o } : e === "disabled" ? l[e] = !!o || !!t : t !== void 0 && (l[e] = t);
1
+ import { jsx as i, jsxs as x, Fragment as w } from "react/jsx-runtime";
2
+ import * as B from "react";
3
+ import { forwardRef as f, Children as L, isValidElement as A, cloneElement as V, useId as W, useMemo as $, createContext as E, useContext as M } from "react";
4
+ import { useFormControl as O } from "@reformer/core";
5
+ import { Checkbox as T } from "./checkbox.js";
6
+ function z(e, n) {
7
+ const l = { ...e };
8
+ for (const r of Object.keys(n)) {
9
+ const o = e[r], t = n[r];
10
+ r.startsWith("on") && typeof o == "function" && typeof t == "function" ? l[r] = (...d) => {
11
+ t(...d), o(...d);
12
+ } : r === "className" && typeof o == "string" && typeof t == "string" ? l[r] = [t, o].filter(Boolean).join(" ") : r === "style" && typeof o == "object" && typeof t == "object" ? l[r] = { ...t, ...o } : r === "disabled" ? l[r] = !!o || !!t : t !== void 0 && (l[r] = t);
13
13
  }
14
14
  return l;
15
15
  }
16
- const p = f(
17
- ({ children: r, ...n }, l) => {
18
- const e = S.only(r);
19
- if (!B(e))
16
+ const b = f(
17
+ ({ children: e, ...n }, l) => {
18
+ const r = L.only(e);
19
+ if (!A(r))
20
20
  return null;
21
- const o = e.props, t = T(n, o), i = e.ref;
22
- return L(e, {
21
+ const o = r.props, t = z(n, o), d = r.ref;
22
+ return V(r, {
23
23
  ...t,
24
- ref: l || i
24
+ ref: l || d
25
25
  });
26
26
  }
27
27
  );
28
- p.displayName = "Slot";
29
- const k = V(null);
30
- function b() {
31
- const r = W(k);
32
- if (!r)
28
+ b.displayName = "Slot";
29
+ const k = E(null);
30
+ function F() {
31
+ const e = M(k);
32
+ if (!e)
33
33
  throw new Error(
34
34
  "FormField.* components must be used within <FormField.Root>. Wrap your field with <FormField.Root control={control}>."
35
35
  );
36
- return r;
36
+ return e;
37
37
  }
38
38
  function N({
39
- control: r,
39
+ control: e,
40
40
  children: n,
41
41
  id: l,
42
- hasDescription: e = !1
42
+ hasDescription: r = !1
43
43
  }) {
44
- const o = A(), t = l ?? o, i = x(
44
+ const o = W(), t = l ?? o, d = $(
45
45
  () => ({
46
46
  controlId: `control-${t}`,
47
47
  labelId: `label-${t}`,
@@ -49,97 +49,97 @@ function N({
49
49
  errorId: `error-${t}`
50
50
  }),
51
51
  [t]
52
- ), s = M(r), c = x(() => {
52
+ ), s = O(e), c = $(() => {
53
53
  const {
54
54
  value: h,
55
55
  errors: a,
56
56
  pending: m,
57
- disabled: y,
58
- valid: v,
59
- invalid: F,
57
+ disabled: v,
58
+ valid: y,
59
+ invalid: I,
60
60
  touched: C,
61
- shouldShowError: I,
62
- componentProps: g
61
+ shouldShowError: g,
62
+ componentProps: p
63
63
  } = s;
64
64
  return {
65
65
  value: h,
66
66
  errors: a,
67
67
  pending: m,
68
- disabled: y,
69
- valid: v,
70
- invalid: F,
68
+ disabled: v,
69
+ valid: y,
70
+ invalid: I,
71
71
  touched: C,
72
- shouldShowError: I,
73
- error: I ? a[0]?.message : void 0,
74
- label: g.label,
75
- required: !!g.required,
76
- componentProps: g,
77
- control: r,
78
- ids: i,
79
- hasDescription: e
72
+ shouldShowError: g,
73
+ error: g ? a[0]?.message : void 0,
74
+ label: p.label,
75
+ required: !!p.required,
76
+ componentProps: p,
77
+ control: e,
78
+ ids: d,
79
+ hasDescription: r
80
80
  };
81
- }, [s, r, i, e]);
82
- return /* @__PURE__ */ d(k.Provider, { value: c, children: n });
81
+ }, [s, e, d, r]);
82
+ return /* @__PURE__ */ i(k.Provider, { value: c, children: n });
83
83
  }
84
84
  N.displayName = "FormField.Root";
85
- const R = f(
86
- ({ asChild: r = !1, children: n, forceRender: l = !1, ...e }, o) => {
87
- const { label: t, required: i, ids: s } = b(), c = n ?? t;
88
- return !c && !l ? null : /* @__PURE__ */ E(r ? p : "label", { ref: o, id: s.labelId, htmlFor: r ? void 0 : s.controlId, ...e, children: [
85
+ const j = f(
86
+ ({ asChild: e = !1, children: n, forceRender: l = !1, ...r }, o) => {
87
+ const { label: t, required: d, ids: s } = F(), c = n ?? t;
88
+ return !c && !l ? null : /* @__PURE__ */ x(e ? b : "label", { ref: o, id: s.labelId, htmlFor: e ? void 0 : s.controlId, ...r, children: [
89
89
  c,
90
- i && /* @__PURE__ */ d("span", { "aria-hidden": "true", children: " *" })
90
+ d && /* @__PURE__ */ i("span", { "aria-hidden": "true", children: " *" })
91
91
  ] });
92
92
  }
93
93
  );
94
- R.displayName = "FormField.Label";
95
- const $ = f(
96
- ({ asChild: r = !1, children: n, ...l }, e) => {
94
+ j.displayName = "FormField.Label";
95
+ const R = f(
96
+ ({ asChild: e = !1, children: n, ...l }, r) => {
97
97
  const {
98
98
  control: o,
99
99
  value: t,
100
- disabled: i,
100
+ disabled: d,
101
101
  shouldShowError: s,
102
102
  errors: c,
103
103
  required: h,
104
104
  ids: a,
105
105
  hasDescription: m,
106
- componentProps: y
107
- } = b(), v = [
106
+ componentProps: v
107
+ } = F(), y = [
108
108
  m ? a.descriptionId : null,
109
109
  s && c.length > 0 ? a.errorId : null
110
- ].filter(Boolean).join(" ") || void 0, F = {
110
+ ].filter(Boolean).join(" ") || void 0, I = {
111
111
  id: a.controlId,
112
112
  "aria-labelledby": a.labelId,
113
113
  "aria-invalid": s ? !0 : void 0,
114
- "aria-describedby": v,
114
+ "aria-describedby": y,
115
115
  "aria-errormessage": s && c.length > 0 ? a.errorId : void 0,
116
116
  "aria-required": h ? !0 : void 0
117
117
  };
118
- if (n || r)
119
- return /* @__PURE__ */ d(p, { ref: e, ...F, ...l, children: n });
120
- const C = o.component;
121
- return /* @__PURE__ */ d(
118
+ if (n || e)
119
+ return /* @__PURE__ */ i(b, { ref: r, ...I, ...l, children: n });
120
+ const C = o.component, { testId: g, ...p } = v ?? {};
121
+ return /* @__PURE__ */ i(
122
122
  C,
123
123
  {
124
- ref: e,
125
- ...y,
126
- ...F,
124
+ ref: r,
125
+ ...p,
126
+ ...I,
127
127
  ...l,
128
128
  value: t,
129
- disabled: i,
130
- onChange: (I) => o.setValue(I),
129
+ disabled: d,
130
+ onChange: (D) => o.setValue(D),
131
131
  onBlur: () => o.markAsTouched()
132
132
  }
133
133
  );
134
134
  }
135
135
  );
136
- $.displayName = "FormField.Control";
137
- const j = f(
138
- ({ asChild: r = !1, multi: n = !1, render: l, children: e, ...o }, t) => {
139
- const { shouldShowError: i, errors: s, ids: c } = b();
140
- if (!i || s.length === 0) return null;
141
- const h = r ? p : "p";
142
- return l ? /* @__PURE__ */ d(w, { children: s.map((a, m) => /* @__PURE__ */ d(
136
+ R.displayName = "FormField.Control";
137
+ const S = f(
138
+ ({ asChild: e = !1, multi: n = !1, render: l, children: r, ...o }, t) => {
139
+ const { shouldShowError: d, errors: s, ids: c } = F();
140
+ if (!d || s.length === 0) return null;
141
+ const h = e ? b : "p";
142
+ return l ? /* @__PURE__ */ i(w, { children: s.map((a, m) => /* @__PURE__ */ i(
143
143
  h,
144
144
  {
145
145
  id: m === 0 ? c.errorId : void 0,
@@ -148,7 +148,7 @@ const j = f(
148
148
  children: l(a, m)
149
149
  },
150
150
  a.code ?? m
151
- )) }) : n ? /* @__PURE__ */ d(w, { children: s.map((a, m) => /* @__PURE__ */ d(
151
+ )) }) : n ? /* @__PURE__ */ i(w, { children: s.map((a, m) => /* @__PURE__ */ i(
152
152
  h,
153
153
  {
154
154
  id: m === 0 ? c.errorId : void 0,
@@ -157,61 +157,63 @@ const j = f(
157
157
  children: a.message
158
158
  },
159
159
  a.code ?? m
160
- )) }) : /* @__PURE__ */ d(h, { ref: t, id: c.errorId, role: "alert", ...o, children: e ?? s[0].message });
160
+ )) }) : /* @__PURE__ */ i(h, { ref: t, id: c.errorId, role: "alert", ...o, children: r ?? s[0].message });
161
161
  }
162
162
  );
163
- j.displayName = "FormField.Error";
163
+ S.displayName = "FormField.Error";
164
164
  const q = f(
165
- ({ asChild: r = !1, children: n, ...l }, e) => {
166
- const { ids: o } = b();
167
- return /* @__PURE__ */ d(r ? p : "p", { ref: e, id: o.descriptionId, ...l, children: n });
165
+ ({ asChild: e = !1, children: n, ...l }, r) => {
166
+ const { ids: o } = F();
167
+ return /* @__PURE__ */ i(e ? b : "p", { ref: r, id: o.descriptionId, ...l, children: n });
168
168
  }
169
169
  );
170
170
  q.displayName = "FormField.Description";
171
171
  const u = N;
172
172
  u.Root = N;
173
- u.Label = R;
174
- u.Control = $;
175
- u.Error = j;
173
+ u.Label = j;
174
+ u.Control = R;
175
+ u.Error = S;
176
176
  u.Description = q;
177
- function z({
178
- className: r,
177
+ const G = (e) => e.message || e.code;
178
+ E(G);
179
+ function H({
180
+ className: e,
179
181
  testIdProp: n,
180
182
  isCheckbox: l,
181
- customChildren: e
183
+ customChildren: r
182
184
  }) {
183
- const { componentProps: o, pending: t } = b(), i = n ?? o?.testId ?? "unknown";
184
- return /* @__PURE__ */ E("div", { className: r, "data-testid": `field-${i}`, children: [
185
- !l && /* @__PURE__ */ d(
185
+ const { componentProps: o, pending: t } = F(), d = n ?? o?.testId ?? "unknown";
186
+ return /* @__PURE__ */ x("div", { className: e, "data-testid": `field-${d}`, children: [
187
+ !l && /* @__PURE__ */ i(
186
188
  u.Label,
187
189
  {
188
190
  className: "block mb-1 text-sm font-medium",
189
- "data-testid": `label-${i}`
191
+ "data-testid": `label-${d}`
190
192
  }
191
193
  ),
192
- e ? /* @__PURE__ */ d(u.Control, { asChild: !0, children: e }) : /* @__PURE__ */ d(u.Control, { "data-testid": `input-${i}` }),
193
- /* @__PURE__ */ d(
194
+ r ? /* @__PURE__ */ i(u.Control, { asChild: !0, children: r }) : /* @__PURE__ */ i(u.Control, { "data-testid": `input-${d}` }),
195
+ /* @__PURE__ */ i(
194
196
  u.Error,
195
197
  {
196
198
  className: "text-destructive text-sm mt-1 block",
197
- "data-testid": `error-${i}`
199
+ "data-testid": `error-${d}`
198
200
  }
199
201
  ),
200
- t && /* @__PURE__ */ d("span", { className: "text-gray-500 text-sm mt-1 block", children: "Проверка..." })
202
+ t && /* @__PURE__ */ i("span", { className: "text-gray-500 text-sm mt-1 block", children: "Проверка..." })
201
203
  ] });
202
204
  }
203
- const G = ({ control: r, className: n, testId: l, children: e }) => {
204
- const o = r.component === O;
205
- return /* @__PURE__ */ d(u.Root, { control: r, children: /* @__PURE__ */ d(
206
- z,
205
+ const J = ({ control: e, className: n, testId: l, children: r }) => {
206
+ const o = e.component === T;
207
+ return /* @__PURE__ */ i(u.Root, { control: e, children: /* @__PURE__ */ i(
208
+ H,
207
209
  {
208
210
  className: n,
209
211
  testIdProp: l,
210
212
  isCheckbox: o,
211
- customChildren: e
213
+ customChildren: r
212
214
  }
213
215
  ) });
214
- }, Q = D.memo(G, (r, n) => r.control === n.control && r.className === n.className && r.testId === n.testId && r.children === n.children);
216
+ }, X = B.memo(J, (e, n) => e.control === n.control && e.className === n.className && e.testId === n.testId && e.children === n.children);
215
217
  export {
216
- Q as FormField
218
+ X as FormField
217
219
  };
package/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
  # AUTO-GENERATED. Edit docs/llms/*.md or JSDoc in src/ and run npm run generate:llms.
3
3
 
4
4
  > Styled form components with Tailwind CSS and Radix UI for @reformer ecosystem
5
- > Package: @reformer/ui-kit • Version: 1.0.0-beta.2
5
+ > Package: @reformer/ui-kit • Version: 5.0.2
6
6
 
7
7
  ## Table of Contents
8
8
  - 01-overview.md — Overview
@@ -2470,7 +2470,7 @@ _Source: src/components/ui/example-card.tsx_
2470
2470
 
2471
2471
  **Signature:**
2472
2472
  ```typescript
2473
- export function FormArraySection<T extends FormFields>({
2473
+ export function FormArraySection<T extends object>({
2474
2474
  control,
2475
2475
  itemComponent: ItemComponent,
2476
2476
  title,
@@ -2482,10 +2482,10 @@ export function FormArraySection<T extends FormFields>({
2482
2482
  hasItems,
2483
2483
  initialValue,
2484
2484
  showRemoveOnSingle = false,
2485
+ reorderable = false,
2485
2486
  maxItems,
2486
2487
  className = 'space-y-3 mt-2',
2487
2488
  cardClassName = 'mb-4 p-4 bg-white rounded border',
2488
- form,
2489
2489
  }: FormArraySectionProps<T>): ReactNode
2490
2490
  ```
2491
2491
 
@@ -2497,12 +2497,9 @@ _Source: src/components/form-array/form-array-section.tsx_
2497
2497
 
2498
2498
  **Signature:**
2499
2499
  ```typescript
2500
- export interface FormArraySectionProps<T extends FormFields> {
2501
- /**
2502
- * Резолвится автоматически: уже-резолвленный ArrayNode/FormArrayProxy ИЛИ
2503
- * FieldPathNode (path.<arrayField>) — в этом случае через `form` + navigator.
2504
- */
2505
- control: FormArrayProxy<T> | ArrayNode<T> | FieldPathNode<unknown, unknown> | undefined;
2500
+ export interface FormArraySectionProps<T extends object> {
2501
+ /** Уже-резолвленный ArrayNode/ModelArrayNode/FormArrayProxy. */
2502
+ control: FormArrayProxy<T> | ArrayNode<T> | undefined;
2506
2503
 
2507
2504
  /** React FC получает `control: FormProxy<T>` для каждого элемента. */
2508
2505
  itemComponent: ComponentType<{ control: FormProxy<T> }>;
@@ -2544,6 +2541,12 @@ export interface FormArraySectionProps<T extends FormFields> {
2544
2541
  /** Показывать «Удалить» когда остался один элемент. По умолчанию `false`. */
2545
2542
  showRemoveOnSingle?: boolean;
2546
2543
 
2544
+ /**
2545
+ * Показывать кнопки ↑/↓ для перестановки элементов. По умолчанию `false`
2546
+ * (обратная совместимость — существующие массивы не меняются).
2547
+ */
2548
+ reorderable?: boolean;
2549
+
2547
2550
  /** Максимум items — AddButton отключается при достижении. */
2548
2551
  maxItems?: number;
2549
2552
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reformer/ui-kit",
3
- "version": "5.0.2",
3
+ "version": "6.0.0",
4
4
  "description": "Styled form components with Tailwind CSS and Radix UI for @reformer ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -126,7 +126,7 @@
126
126
  "@reformer/cdk": ">=1.0.0",
127
127
  "@reformer/core": ">=1.1.0",
128
128
  "@reformer/renderer-react": ">=1.0.0",
129
- "lucide-react": "^0.400.0",
129
+ "lucide-react": ">=0.400.0 <1.0.0",
130
130
  "react": "^18.0.0 || ^19.0.0",
131
131
  "react-dom": "^18.0.0 || ^19.0.0"
132
132
  },
@@ -1,92 +0,0 @@
1
- import { jsx as e, jsxs as i } from "react/jsx-runtime";
2
- import { useFormControl as S, extractPath as $, FieldPathNavigator as P } from "@reformer/core";
3
- import { FormArray as s } from "@reformer/cdk/form-array";
4
- const R = new P();
5
- function B(t, a) {
6
- if (!t) return null;
7
- if (typeof t == "object" && typeof t.push == "function" && typeof t.removeAt == "function")
8
- return t;
9
- if (!a)
10
- return typeof console < "u" && console.warn(
11
- "[FormArraySection] control is a FieldPath but no `form` prop available. Ensure this component is rendered inside FormRenderer with form available, or pass form explicitly."
12
- ), null;
13
- try {
14
- const r = $(t), n = R.getNodeByPath(a, r);
15
- return n || (typeof console < "u" && console.warn(`[FormArraySection] No ArrayNode at path "${r}".`), null);
16
- } catch (r) {
17
- return typeof console < "u" && console.warn("[FormArraySection] Failed to resolve control:", r), null;
18
- }
19
- }
20
- function E({
21
- control: t,
22
- itemComponent: a,
23
- title: r,
24
- itemLabel: n,
25
- addButtonLabel: u = "+ Добавить",
26
- removeButtonLabel: v = "Удалить",
27
- emptyMessage: f,
28
- emptyMessageHint: m,
29
- hasItems: b,
30
- initialValue: h,
31
- showRemoveOnSingle: g = !1,
32
- maxItems: p,
33
- className: x = "space-y-3 mt-2",
34
- cardClassName: N = "mb-4 p-4 bg-white rounded border",
35
- form: w
36
- }) {
37
- const d = B(t, w), A = S(
38
- d ?? void 0
39
- );
40
- if (b === !1 || !d) return null;
41
- const c = A?.length ?? 0, y = p != null && c >= p, F = (l, o) => typeof n == "function" ? n(l, o) : `${n ?? r ?? "Элемент"} #${o + 1}`;
42
- return /* @__PURE__ */ e(s.Root, { control: d, children: /* @__PURE__ */ i("section", { className: x, children: [
43
- r ? /* @__PURE__ */ i("div", { className: "flex justify-between items-center mb-4", children: [
44
- /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: r }),
45
- y ? null : /* @__PURE__ */ e(
46
- s.AddButton,
47
- {
48
- initialValue: h,
49
- "data-testid": "array-add",
50
- className: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground shadow hover:bg-primary/90 h-9 px-4 py-2",
51
- children: u
52
- }
53
- )
54
- ] }) : null,
55
- /* @__PURE__ */ e(s.List, { className: "space-y-3", children: ({ control: l, index: o, remove: j }) => {
56
- const C = g || c > 1;
57
- return /* @__PURE__ */ i("div", { className: N, "data-testid": `array-item-${o}`, children: [
58
- r || n ? /* @__PURE__ */ i("div", { className: "flex justify-between items-center mb-3", children: [
59
- /* @__PURE__ */ e("h4", { className: "font-medium", children: F(l, o) }),
60
- C ? /* @__PURE__ */ e(
61
- "button",
62
- {
63
- type: "button",
64
- onClick: j,
65
- "data-testid": `array-item-${o}-remove`,
66
- className: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-destructive text-destructive-foreground shadow hover:bg-destructive/90 h-9 px-4 py-2",
67
- children: v
68
- }
69
- ) : null
70
- ] }) : null,
71
- /* @__PURE__ */ e(a, { control: l })
72
- ] });
73
- } }),
74
- c === 0 && f ? /* @__PURE__ */ e(s.Empty, { children: /* @__PURE__ */ i("div", { className: "p-4 bg-gray-100 border border-gray-300 rounded text-center text-gray-600", children: [
75
- f,
76
- m && /* @__PURE__ */ e("div", { className: "mt-2 text-xs text-gray-500", children: m })
77
- ] }) }) : null,
78
- !r && !y ? /* @__PURE__ */ e("div", { children: /* @__PURE__ */ e(
79
- s.AddButton,
80
- {
81
- initialValue: h,
82
- "data-testid": "array-add",
83
- className: "text-sm text-blue-600 hover:text-blue-700 hover:underline",
84
- children: u
85
- }
86
- ) }) : null
87
- ] }) });
88
- }
89
- E.__selfManagedChildren = !0;
90
- export {
91
- E as F
92
- };