@ryuzaki13/react-foundation-ui 1.5.10 → 1.5.11
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.
- package/dist/advanced-search/index.js +182 -148
- package/dist/advanced-search/index.js.map +1 -1
- package/dist/advanced-search/model/useAdvancedSearchSelection.d.ts +1 -1
- package/dist/advanced-search/model/useAdvancedSearchSelection.d.ts.map +1 -1
- package/dist/advanced-search/ui/AdvancedSearchModal.d.ts +12 -3
- package/dist/advanced-search/ui/AdvancedSearchModal.d.ts.map +1 -1
- package/dist/advanced-search/ui/AdvancedSearchModalSession.d.ts +15 -0
- package/dist/advanced-search/ui/AdvancedSearchModalSession.d.ts.map +1 -0
- package/dist/advanced-search/ui/AdvancedSearchSelect.d.ts.map +1 -1
- package/dist/badge/index.js +1 -1
- package/dist/base-table/index.js +1 -1
- package/dist/button/index.js +1 -1
- package/dist/check-box/index.js +1 -1
- package/dist/chunks/{CalendarView-BSshXZAX.js → CalendarView-DqA-PbAU.js} +42 -42
- package/dist/chunks/{CalendarView-BSshXZAX.js.map → CalendarView-DqA-PbAU.js.map} +1 -1
- package/dist/chunks/{ImageViewerRuntime-BMs3BBkV.js → ImageViewerRuntime-HGBWp9NM.js} +18 -18
- package/dist/chunks/{ImageViewerRuntime-BMs3BBkV.js.map → ImageViewerRuntime-HGBWp9NM.js.map} +1 -1
- package/dist/chunks/{TextEditorLexicalClient-CLjpATRu.js → TextEditorLexicalClient-B084kVzu.js} +322 -322
- package/dist/chunks/{TextEditorLexicalClient-CLjpATRu.js.map → TextEditorLexicalClient-B084kVzu.js.map} +1 -1
- package/dist/chunks/{badge-CaooZXYD.js → badge-DMM8cOHd.js} +16 -16
- package/dist/chunks/{badge-CaooZXYD.js.map → badge-DMM8cOHd.js.map} +1 -1
- package/dist/chunks/{base-table-fUJY__6M.js → base-table-CJdKCKhb.js} +125 -125
- package/dist/chunks/{base-table-fUJY__6M.js.map → base-table-CJdKCKhb.js.map} +1 -1
- package/dist/chunks/{button-C--u7uJj.js → button-CzUO5rzq.js} +17 -17
- package/dist/chunks/{button-C--u7uJj.js.map → button-CzUO5rzq.js.map} +1 -1
- package/dist/chunks/{check-box-BZiEBR9c.js → check-box-sfGjYczQ.js} +11 -11
- package/dist/chunks/{check-box-BZiEBR9c.js.map → check-box-sfGjYczQ.js.map} +1 -1
- package/dist/chunks/{context-menu-XZ-e0f6_.js → context-menu-6nJkPmKV.js} +83 -83
- package/dist/chunks/{context-menu-XZ-e0f6_.js.map → context-menu-6nJkPmKV.js.map} +1 -1
- package/dist/chunks/date-input-9T8AoG5U.js +341 -0
- package/dist/chunks/{date-input-Dr5QNVT6.js.map → date-input-9T8AoG5U.js.map} +1 -1
- package/dist/chunks/{input-BNszawm6.js → input-VC8jEoTa.js} +37 -37
- package/dist/chunks/{input-BNszawm6.js.map → input-VC8jEoTa.js.map} +1 -1
- package/dist/chunks/{input-files-DxLVr2PF.js → input-files-CoMjadk7.js} +22 -22
- package/dist/chunks/{input-files-DxLVr2PF.js.map → input-files-CoMjadk7.js.map} +1 -1
- package/dist/chunks/{input-search-C2OOhZlw.js → input-search-Bs_9SxT0.js} +2 -2
- package/dist/chunks/{input-search-C2OOhZlw.js.map → input-search-Bs_9SxT0.js.map} +1 -1
- package/dist/chunks/{misc-Cp_r022Z.js → misc-CkRRma5E.js} +55 -55
- package/dist/chunks/{misc-Cp_r022Z.js.map → misc-CkRRma5E.js.map} +1 -1
- package/dist/chunks/{modal-DK9M7lT5.js → modal-LHkNxBko.js} +38 -38
- package/dist/chunks/{modal-DK9M7lT5.js.map → modal-LHkNxBko.js.map} +1 -1
- package/dist/chunks/{multi-select-CFHaQPot.js → multi-select-BJ6Lst1E.js} +44 -44
- package/dist/chunks/{multi-select-CFHaQPot.js.map → multi-select-BJ6Lst1E.js.map} +1 -1
- package/dist/chunks/{picker-D3Fc15JH.js → picker-DLnfOgZj.js} +34 -34
- package/dist/chunks/{picker-D3Fc15JH.js.map → picker-DLnfOgZj.js.map} +1 -1
- package/dist/chunks/{popover-BTT7zSqx.js → popover-IExDrsF6.js} +67 -67
- package/dist/chunks/{popover-BTT7zSqx.js.map → popover-IExDrsF6.js.map} +1 -1
- package/dist/chunks/{select-BolFlcoY.js → select-CysiIrej.js} +5 -5
- package/dist/chunks/{select-BolFlcoY.js.map → select-CysiIrej.js.map} +1 -1
- package/dist/chunks/{sortable-DcTIAbjD.js → sortable-BKIwlqhI.js} +17 -17
- package/dist/chunks/{sortable-DcTIAbjD.js.map → sortable-BKIwlqhI.js.map} +1 -1
- package/dist/chunks/{table-BChs6bgR.js → table-CIWdAcPY.js} +2 -2
- package/dist/chunks/{table-BChs6bgR.js.map → table-CIWdAcPY.js.map} +1 -1
- package/dist/chunks/{types-CxXyru6U.js → types-D9Wj9wrg.js} +21 -21
- package/dist/chunks/{types-CxXyru6U.js.map → types-D9Wj9wrg.js.map} +1 -1
- package/dist/chunks/{useODataSelectBase-BhCMCzv_.js → useODataSelectBase-Bq36Wp1U.js} +8 -8
- package/dist/chunks/{useODataSelectBase-BhCMCzv_.js.map → useODataSelectBase-Bq36Wp1U.js.map} +1 -1
- package/dist/context-menu/index.js +1 -1
- package/dist/date-input/index.js +2 -2
- package/dist/date-time-input/index.js +32 -32
- package/dist/dialog/index.js +9 -9
- package/dist/disclosure/index.js +10 -10
- package/dist/drop-zone/index.js +8 -8
- package/dist/errors/index.js +8 -8
- package/dist/expandable-action-panel/index.js +16 -16
- package/dist/flex/useFlexClasses.d.ts +1 -1
- package/dist/fullscreen-portal/index.js +7 -7
- package/dist/image/index.js +27 -27
- package/dist/input/index.js +1 -1
- package/dist/input-date-time/index.js +1 -1
- package/dist/input-file/index.js +1 -1
- package/dist/input-files/index.js +1 -1
- package/dist/input-image/index.js +1 -1
- package/dist/input-search/index.js +1 -1
- package/dist/layout-picker/index.js +1 -1
- package/dist/list/index.js +25 -25
- package/dist/listbox/index.js +5 -5
- package/dist/misc/index.js +1 -1
- package/dist/modal/index.js +1 -1
- package/dist/multi-select/index.js +1 -1
- package/dist/notifications/index.js +30 -30
- package/dist/picker/index.js +1 -1
- package/dist/popover/index.js +1 -1
- package/dist/preset-range-date-input/index.js +1 -1
- package/dist/preset-select/index.js +1 -1
- package/dist/radio-button/index.js +10 -10
- package/dist/select/index.js +1 -1
- package/dist/simple-table/index.js +1 -1
- package/dist/slider/index.js +3 -3
- package/dist/sortable/index.js +1 -1
- package/dist/state-select/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/switch/index.js +11 -11
- package/dist/table/index.js +1 -1
- package/dist/table-sorting/index.js +13 -13
- package/dist/tabs/index.js +1 -1
- package/dist/tag-input/index.js +55 -55
- package/dist/tag-input/index.js.map +1 -1
- package/dist/text/index.js +1 -1
- package/dist/text-editor/index.js +3 -3
- package/dist/textarea/index.js +1 -1
- package/dist/tree-select/index.js +134 -134
- package/dist/tree-select/index.js.map +1 -1
- package/dist/tree-table/index.js +16 -16
- package/package.json +1 -1
- package/storybook-static/project.json +1 -1
- package/dist/chunks/date-input-Dr5QNVT6.js +0 -341
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as e } from "./ui.module-DHVTOqZ0.js";
|
|
2
2
|
import { useId as t, useLayoutEffect as n, useRef as r, useState as i } from "react";
|
|
3
3
|
import { cn as a } from "@ryuzaki13/react-foundation-lib/utils";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
5
|
+
import { XIcon as c } from "lucide-react";
|
|
6
6
|
import { toFiniteNumber as l } from "@ryuzaki13/react-foundation-lib/formatters";
|
|
7
7
|
var u = {
|
|
8
8
|
controlLayout: "lRn5ye",
|
|
@@ -29,82 +29,82 @@ function d({ id: e, hasLabel: n, hasDescription: r, hasError: i }) {
|
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/input/Input.tsx
|
|
31
31
|
function f({ label: t, controlId: n, labelId: r }) {
|
|
32
|
-
return /* @__PURE__ */
|
|
32
|
+
return /* @__PURE__ */ o("label", {
|
|
33
33
|
id: r,
|
|
34
34
|
htmlFor: n,
|
|
35
35
|
className: e.uiLabel,
|
|
36
36
|
children: t
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
-
function p({ label: t, description: n, size: r, disabled: i, className:
|
|
39
|
+
function p({ label: t, description: n, size: r, disabled: i, className: c }) {
|
|
40
40
|
let { controlId: l, labelId: u, descriptionId: p } = d({
|
|
41
41
|
hasLabel: t != null,
|
|
42
42
|
hasDescription: !!n
|
|
43
43
|
});
|
|
44
|
-
return /* @__PURE__ */
|
|
45
|
-
className: a(e.uiElement, e.uiSizable, r && e[r], e.disabled,
|
|
44
|
+
return /* @__PURE__ */ s("div", {
|
|
45
|
+
className: a(e.uiElement, e.uiSizable, r && e[r], e.disabled, c),
|
|
46
46
|
"aria-disabled": i || void 0,
|
|
47
47
|
children: [
|
|
48
|
-
t && /* @__PURE__ */
|
|
48
|
+
t && /* @__PURE__ */ o(f, {
|
|
49
49
|
label: t,
|
|
50
50
|
controlId: l,
|
|
51
51
|
labelId: u
|
|
52
52
|
}),
|
|
53
|
-
n && /* @__PURE__ */
|
|
53
|
+
n && /* @__PURE__ */ o("p", {
|
|
54
54
|
id: p,
|
|
55
55
|
className: e.uiDescription,
|
|
56
56
|
children: n
|
|
57
57
|
}),
|
|
58
|
-
/* @__PURE__ */
|
|
58
|
+
/* @__PURE__ */ o("div", { className: a(e.uiInputControlFake, "skeletonLine") })
|
|
59
59
|
]
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
function m({ label: t, description: n, disabled: r, className: i, error:
|
|
63
|
-
return /* @__PURE__ */
|
|
62
|
+
function m({ label: t, description: n, disabled: r, className: i, error: c, size: l, controlId: d, labelId: p, descriptionId: m, errorId: h, required: g, children: _ }) {
|
|
63
|
+
return /* @__PURE__ */ s("div", {
|
|
64
64
|
className: a(e.uiElement, e.uiSizable, l && e[l], r && e.disabled, g && e.required, i),
|
|
65
65
|
"aria-disabled": r || void 0,
|
|
66
66
|
children: [
|
|
67
|
-
t && /* @__PURE__ */
|
|
67
|
+
t && /* @__PURE__ */ o(f, {
|
|
68
68
|
label: t,
|
|
69
69
|
controlId: d,
|
|
70
70
|
labelId: p
|
|
71
71
|
}),
|
|
72
|
-
n && /* @__PURE__ */
|
|
72
|
+
n && /* @__PURE__ */ o("p", {
|
|
73
73
|
id: m,
|
|
74
74
|
className: e.uiDescription,
|
|
75
75
|
children: n
|
|
76
76
|
}),
|
|
77
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ s("div", {
|
|
78
78
|
className: u.controlLayout,
|
|
79
|
-
children: [_,
|
|
79
|
+
children: [_, c && /* @__PURE__ */ o("p", {
|
|
80
80
|
id: h,
|
|
81
81
|
className: u.error,
|
|
82
82
|
role: "alert",
|
|
83
|
-
children:
|
|
83
|
+
children: c
|
|
84
84
|
})]
|
|
85
85
|
})
|
|
86
86
|
]
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
function h({ className: e, endAdornment: t, endAdornmentClassName: n, endAdornmentWidth: r, children: i }) {
|
|
90
|
-
let
|
|
91
|
-
return /* @__PURE__ */
|
|
90
|
+
let c = t != null, l = c && r ? { "--input-end-adornment-width": r } : void 0;
|
|
91
|
+
return /* @__PURE__ */ s("div", {
|
|
92
92
|
className: a(u.controlLayout, e),
|
|
93
93
|
style: l,
|
|
94
|
-
children: [i({ controlClassName:
|
|
94
|
+
children: [i({ controlClassName: c ? u.controlWithEndAdornment : void 0 }), c && /* @__PURE__ */ o("div", {
|
|
95
95
|
className: a(u.controlEndAdornment, n),
|
|
96
96
|
children: t
|
|
97
97
|
})]
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
function g({ onClick: e, disabled: t, className: n, ariaLabel: r = "Очистить значение" }) {
|
|
101
|
-
return /* @__PURE__ */
|
|
101
|
+
return /* @__PURE__ */ o("button", {
|
|
102
102
|
type: "button",
|
|
103
103
|
disabled: t || !e,
|
|
104
104
|
className: a(u.clearButton, n),
|
|
105
105
|
onClick: e,
|
|
106
106
|
"aria-label": r,
|
|
107
|
-
children: /* @__PURE__ */
|
|
107
|
+
children: /* @__PURE__ */ o(c, {})
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
function _(e, t, n) {
|
|
@@ -113,15 +113,15 @@ function _(e, t, n) {
|
|
|
113
113
|
if (e || t) return "var(--control-height)";
|
|
114
114
|
}
|
|
115
115
|
function v({ endAdornment: e, onClear: t, disabled: n }) {
|
|
116
|
-
if (!(e == null && !t)) return /* @__PURE__ */
|
|
116
|
+
if (!(e == null && !t)) return /* @__PURE__ */ s("div", {
|
|
117
117
|
className: u.endAdornmentGroup,
|
|
118
|
-
children: [e, t && /* @__PURE__ */
|
|
118
|
+
children: [e, t && /* @__PURE__ */ o(g, {
|
|
119
119
|
onClick: t,
|
|
120
120
|
disabled: n
|
|
121
121
|
})]
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
-
function y({ label: t, description: n, disabled: r, className:
|
|
124
|
+
function y({ label: t, description: n, disabled: r, className: s, onClear: c, error: l, onClearError: f, onChange: p, value: g, size: y, allowedPattern: b, endAdornment: x, endAdornmentClassName: S, endAdornmentWidth: C, type: w = "text", ...T }) {
|
|
125
125
|
let [E, D] = i(!1), O = (e) => {
|
|
126
126
|
let t = e.target.value;
|
|
127
127
|
b !== void 0 && D(t !== "" && !b.test(t)), p(t), l && f && f();
|
|
@@ -135,23 +135,23 @@ function y({ label: t, description: n, disabled: r, className: o, onClear: c, er
|
|
|
135
135
|
onClear: c,
|
|
136
136
|
disabled: r || !k
|
|
137
137
|
}), R = _(x != null, !!c, C);
|
|
138
|
-
return /* @__PURE__ */
|
|
138
|
+
return /* @__PURE__ */ o(m, {
|
|
139
139
|
label: t,
|
|
140
140
|
description: n,
|
|
141
141
|
disabled: r,
|
|
142
142
|
required: T.required,
|
|
143
|
-
className:
|
|
143
|
+
className: s,
|
|
144
144
|
size: y,
|
|
145
145
|
error: A,
|
|
146
146
|
controlId: j,
|
|
147
147
|
labelId: M,
|
|
148
148
|
descriptionId: N,
|
|
149
149
|
errorId: P,
|
|
150
|
-
children: /* @__PURE__ */
|
|
150
|
+
children: /* @__PURE__ */ o(h, {
|
|
151
151
|
endAdornment: L,
|
|
152
152
|
endAdornmentClassName: S,
|
|
153
153
|
endAdornmentWidth: R,
|
|
154
|
-
children: ({ controlClassName: t }) => /* @__PURE__ */
|
|
154
|
+
children: ({ controlClassName: t }) => /* @__PURE__ */ o("input", {
|
|
155
155
|
...T,
|
|
156
156
|
id: j,
|
|
157
157
|
type: w,
|
|
@@ -167,7 +167,7 @@ function y({ label: t, description: n, disabled: r, className: o, onClear: c, er
|
|
|
167
167
|
})
|
|
168
168
|
});
|
|
169
169
|
}
|
|
170
|
-
function b({ label: t, description: i, disabled:
|
|
170
|
+
function b({ label: t, description: i, disabled: s, className: c, onClear: f, error: p, onClearError: g, onChange: y, value: b, size: x, endAdornment: S, endAdornmentClassName: C, endAdornmentWidth: w, ...T }) {
|
|
171
171
|
let E = T, { controlId: D, labelId: O, descriptionId: k, errorId: A, describedBy: j } = d({
|
|
172
172
|
id: T.id,
|
|
173
173
|
hasLabel: t != null,
|
|
@@ -193,12 +193,12 @@ function b({ label: t, description: i, disabled: o, className: c, onClear: f, er
|
|
|
193
193
|
}, L = v({
|
|
194
194
|
endAdornment: S,
|
|
195
195
|
onClear: F,
|
|
196
|
-
disabled:
|
|
196
|
+
disabled: s
|
|
197
197
|
}), R = _(S != null, !!F, w);
|
|
198
|
-
return /* @__PURE__ */
|
|
198
|
+
return /* @__PURE__ */ o(m, {
|
|
199
199
|
label: t,
|
|
200
200
|
description: i,
|
|
201
|
-
disabled:
|
|
201
|
+
disabled: s,
|
|
202
202
|
required: T.required,
|
|
203
203
|
className: c,
|
|
204
204
|
size: x,
|
|
@@ -207,17 +207,17 @@ function b({ label: t, description: i, disabled: o, className: c, onClear: f, er
|
|
|
207
207
|
labelId: O,
|
|
208
208
|
descriptionId: k,
|
|
209
209
|
errorId: A,
|
|
210
|
-
children: /* @__PURE__ */
|
|
210
|
+
children: /* @__PURE__ */ o(h, {
|
|
211
211
|
endAdornment: L,
|
|
212
212
|
endAdornmentClassName: C,
|
|
213
213
|
endAdornmentWidth: R,
|
|
214
|
-
children: ({ controlClassName: t }) => /* @__PURE__ */
|
|
214
|
+
children: ({ controlClassName: t }) => /* @__PURE__ */ o("input", {
|
|
215
215
|
...E,
|
|
216
216
|
ref: M,
|
|
217
217
|
id: D,
|
|
218
218
|
type: "number",
|
|
219
219
|
inputMode: "decimal",
|
|
220
|
-
disabled:
|
|
220
|
+
disabled: s,
|
|
221
221
|
defaultValue: P,
|
|
222
222
|
"aria-labelledby": O,
|
|
223
223
|
"aria-describedby": j,
|
|
@@ -231,7 +231,7 @@ var x = y;
|
|
|
231
231
|
//#endregion
|
|
232
232
|
//#region src/input/InputErrorComponent.tsx
|
|
233
233
|
function S({ text: t }) {
|
|
234
|
-
return /* @__PURE__ */
|
|
234
|
+
return /* @__PURE__ */ o("div", {
|
|
235
235
|
className: e.uiErrorElement,
|
|
236
236
|
children: t
|
|
237
237
|
});
|
|
@@ -239,4 +239,4 @@ function S({ text: t }) {
|
|
|
239
239
|
//#endregion
|
|
240
240
|
export { b as a, p as c, h as i, d as l, x as n, y as o, g as r, m as s, S as t, u };
|
|
241
241
|
|
|
242
|
-
//# sourceMappingURL=input-
|
|
242
|
+
//# sourceMappingURL=input-VC8jEoTa.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-BNszawm6.js","names":[],"sources":["../../src/input/Input.module.scss","../../src/input/lib/useInputFieldIds.ts","../../src/input/Input.tsx","../../src/input/InputErrorComponent.tsx"],"sourcesContent":["@use \"@/styles/mixins\" as *;\n\n.controlLayout {\n\tposition: relative;\n\twidth: 100%;\n}\n\n.input {\n\theight: var(--control-height);\n\twidth: 100%;\n\tpadding: var(--space-xs) var(--space-sm);\n\tfont-size: var(--font-size-md);\n\n\t--ui-color: var(--content-0);\n\t--ui-color-hover: var(--content-0);\n\t--ui-color-active: var(--content-0);\n\n\t--button-size: cacl(var(--control-height) - var(--border-width) * 2);\n}\n\n.inputNumber {\n\tappearance: textfield;\n\t-moz-appearance: textfield;\n\n\t&::-webkit-inner-spin-button,\n\t&::-webkit-outer-spin-button {\n\t\t-webkit-appearance: none;\n\t\tmargin: 0;\n\t}\n}\n\n.controlWithEndAdornment {\n\tpadding-right: calc(var(--input-end-adornment-width, var(--control-height)) + var(--space-sm));\n}\n\n.controlEndAdornment {\n\tposition: absolute;\n\theight: 100%;\n\ttop: 0;\n\tright: calc(var(--border-width) * 2);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n\tmax-width: calc(100% - var(--space-md));\n}\n\n.endAdornmentGroup {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: var(--space-xs);\n}\n\n.clearButton {\n\twidth: var(--button-size);\n\theight: var(--button-size);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder: none;\n\tborder-radius: var(--radius-sm);\n\n\tbackground: transparent;\n\tcolor: var(--content-2);\n\n\ttransition:\n\t\tcolor var(--transition-fast) ease,\n\t\tbackground-color var(--transition-fast) ease;\n\n\t&:hover {\n\t\tcolor: var(--content-accent);\n\t}\n\n\t&:focus-visible {\n\t\t@include focusVisible;\n\t}\n\n\t&:disabled {\n\t\tpointer-events: none;\n\t\topacity: var(--disabled-opacity);\n\t}\n}\n\n.error {\n\tposition: absolute;\n\tz-index: 11;\n\tbottom: calc(var(--space-xs) * -1);\n\tleft: 0;\n\tright: 0;\n\ttransform: translateY(100%);\n\t// font-size: var(--font-size-sm);\n\tcolor: var(--status-error-on-fill);\n\tbackground: var(--status-error-fill);\n\tborder-radius: var(--radius-sm);\n\tpadding: var(--space-xs) var(--space-sm);\n\tbox-shadow: var(--shadow-sm);\n\topacity: 0.9;\n}\n","import { useId } from \"react\";\n\nexport interface UseInputFieldIdsOptions {\n\tid?: string;\n\thasLabel?: boolean;\n\thasDescription?: boolean;\n\thasError?: boolean;\n}\n\nexport interface InputFieldIds {\n\tcontrolId: string;\n\tlabelId?: string;\n\tdescriptionId?: string;\n\terrorId?: string;\n\tdescribedBy?: string;\n}\n\nexport function useInputFieldIds({ id: externalId, hasLabel, hasDescription, hasError }: UseInputFieldIdsOptions): InputFieldIds {\n\tconst autoId = useId();\n\tconst controlId = externalId ?? autoId;\n\tconst labelId = hasLabel ? `${controlId}-label` : undefined;\n\tconst descriptionId = hasDescription ? `${controlId}-description` : undefined;\n\tconst errorId = hasError ? `${controlId}-error` : undefined;\n\tconst describedBy = [descriptionId, errorId].filter(Boolean).join(\" \") || undefined;\n\n\treturn {\n\t\tcontrolId,\n\t\tlabelId,\n\t\tdescriptionId,\n\t\terrorId,\n\t\tdescribedBy\n\t};\n}\n","import React, { CSSProperties, InputHTMLAttributes, ReactNode, useLayoutEffect, useRef, useState } from \"react\";\n\nimport { toFiniteNumber } from \"@ryuzaki13/react-foundation-lib/formatters\";\nimport { cn } from \"@ryuzaki13/react-foundation-lib/utils\";\nimport { XIcon } from \"lucide-react\";\n\nimport { UiBaseProps, UiSize } from \"../types\";\nimport uiStyles from \"../ui.module.scss\";\n\nimport styles from \"./Input.module.scss\";\nimport { useInputFieldIds } from \"./lib/useInputFieldIds\";\n\nexport interface InputUIProps extends Omit<UiBaseProps<never>, \"value\" | \"onChange\"> {\n\tclassName?: string;\n\terror?: string;\n\tcontrolId?: string;\n\tlabelId?: string;\n\tdescriptionId?: string;\n\terrorId?: string;\n\trequired?: boolean;\n\tchildren: React.ReactNode;\n}\n\nexport interface InputControlRenderProps {\n\tcontrolClassName?: string;\n}\n\nexport interface InputControlProps {\n\tclassName?: string;\n\tendAdornment?: ReactNode;\n\tendAdornmentClassName?: string;\n\tendAdornmentWidth?: string;\n\tchildren: (props: InputControlRenderProps) => ReactNode;\n}\n\nexport interface InputClearButtonProps {\n\tonClick?: () => void;\n\tdisabled?: boolean;\n\tclassName?: string;\n\tariaLabel?: string;\n}\n\nfunction InputLabel({ label, controlId, labelId }: { label: React.ReactNode; controlId?: string; labelId?: string }) {\n\treturn (\n\t\t<label id={labelId} htmlFor={controlId} className={uiStyles.uiLabel}>\n\t\t\t{label}\n\t\t</label>\n\t);\n}\n\nexport function InputUILoading({\n\tlabel,\n\tdescription,\n\tsize,\n\tdisabled,\n\tclassName\n}: Pick<InputUIProps, \"label\" | \"description\" | \"size\" | \"disabled\" | \"className\">) {\n\tconst { controlId, labelId, descriptionId } = useInputFieldIds({\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description\n\t});\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(uiStyles.uiElement, uiStyles.uiSizable, size && uiStyles[size as UiSize], uiStyles.disabled, className)}\n\t\t\taria-disabled={disabled || undefined}>\n\t\t\t{label && <InputLabel label={label} controlId={controlId} labelId={labelId} />}\n\t\t\t{description && (\n\t\t\t\t<p id={descriptionId} className={uiStyles.uiDescription}>\n\t\t\t\t\t{description}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<div className={cn(uiStyles.uiInputControlFake, \"skeletonLine\")} />\n\t\t</div>\n\t);\n}\n\n/**\n * Низкоуровневая оболочка поля ввода с общими элементами оформления.\n */\nexport function InputUI({\n\tlabel,\n\tdescription,\n\tdisabled,\n\tclassName,\n\terror,\n\tsize,\n\tcontrolId,\n\tlabelId,\n\tdescriptionId,\n\terrorId,\n\trequired,\n\tchildren\n}: InputUIProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\tuiStyles.uiElement,\n\t\t\t\tuiStyles.uiSizable,\n\t\t\t\tsize && uiStyles[size as UiSize],\n\t\t\t\tdisabled && uiStyles.disabled,\n\t\t\t\trequired && uiStyles.required,\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\taria-disabled={disabled || undefined}>\n\t\t\t{label && <InputLabel label={label} controlId={controlId} labelId={labelId} />}\n\t\t\t{description && (\n\t\t\t\t<p id={descriptionId} className={uiStyles.uiDescription}>\n\t\t\t\t\t{description}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<div className={styles.controlLayout}>\n\t\t\t\t{children}\n\n\t\t\t\t{error && (\n\t\t\t\t\t<p id={errorId} className={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function InputControl({ className, endAdornment, endAdornmentClassName, endAdornmentWidth, children }: InputControlProps) {\n\tconst hasEndAdornment = endAdornment !== undefined && endAdornment !== null;\n\tconst style =\n\t\thasEndAdornment && endAdornmentWidth ? ({ \"--input-end-adornment-width\": endAdornmentWidth } as CSSProperties) : undefined;\n\n\treturn (\n\t\t<div className={cn(styles.controlLayout, className)} style={style}>\n\t\t\t{children({ controlClassName: hasEndAdornment ? styles.controlWithEndAdornment : undefined })}\n\t\t\t{hasEndAdornment && <div className={cn(styles.controlEndAdornment, endAdornmentClassName)}>{endAdornment}</div>}\n\t\t</div>\n\t);\n}\n\nexport function InputClearButton({ onClick, disabled, className, ariaLabel = \"Очистить значение\" }: InputClearButtonProps) {\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tdisabled={disabled || !onClick}\n\t\t\tclassName={cn(styles.clearButton, className)}\n\t\t\tonClick={onClick}\n\t\t\taria-label={ariaLabel}>\n\t\t\t<XIcon />\n\t\t</button>\n\t);\n}\n\nfunction resolveEndAdornmentWidth(hasAdornment: boolean, hasClearAction: boolean, explicitWidth?: string) {\n\tif (explicitWidth) {\n\t\treturn explicitWidth;\n\t}\n\n\tif (hasAdornment && hasClearAction) {\n\t\treturn \"calc((var(--control-height) * 2) + var(--space-xs))\";\n\t}\n\n\tif (hasAdornment || hasClearAction) {\n\t\treturn \"var(--control-height)\";\n\t}\n\n\treturn undefined;\n}\n\nfunction createEndAdornment({ endAdornment, onClear, disabled }: { endAdornment?: ReactNode; onClear?: () => void; disabled?: boolean }) {\n\tif ((endAdornment === undefined || endAdornment === null) && !onClear) {\n\t\treturn undefined;\n\t}\n\n\treturn (\n\t\t<div className={styles.endAdornmentGroup}>\n\t\t\t{endAdornment}\n\t\t\t{onClear && <InputClearButton onClick={onClear} disabled={disabled} />}\n\t\t</div>\n\t);\n}\n\nexport interface BaseInputProps<T extends number | string | undefined, V = T>\n\textends Omit<InputHTMLAttributes<HTMLInputElement>, \"size\" | \"value\" | \"type\" | \"defaultValue\" | \"onChange\">, UiBaseProps<T, V> {\n\tonClear?: () => void;\n\terror?: string;\n\tonClearError?: () => void;\n\tdefaultValue?: T;\n\tendAdornment?: ReactNode;\n\tendAdornmentClassName?: string;\n\tendAdornmentWidth?: string;\n}\n\nexport interface InputTextProps extends BaseInputProps<string> {\n\ttype?: \"email\" | \"password\" | \"search\" | \"tel\" | \"text\" | \"url\";\n\n\t/**\n\t * Регулярное выражение для валидации всего введённого значения.\n\t * При несоответствии поле подсвечивается как невалидное, но текст не удаляется.\n\t * Пример: `/^\\d*$/`\n\t */\n\tallowedPattern?: RegExp;\n}\n\n/**\n * Базовое текстовое поле ввода для строковых значений.\n */\nexport function InputText({\n\tlabel,\n\tdescription,\n\tdisabled,\n\tclassName,\n\tonClear,\n\terror,\n\tonClearError,\n\tonChange,\n\tvalue,\n\tsize,\n\tallowedPattern,\n\tendAdornment,\n\tendAdornmentClassName,\n\tendAdornmentWidth,\n\ttype = \"text\",\n\t...props\n}: InputTextProps) {\n\tconst [internalInvalid, setInternalInvalid] = useState(false);\n\n\tconst handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst newValue = e.target.value;\n\n\t\tif (allowedPattern !== undefined) {\n\t\t\tsetInternalInvalid(newValue !== \"\" && !allowedPattern.test(newValue));\n\t\t}\n\n\t\tonChange(newValue);\n\t\tif (error && onClearError) onClearError();\n\t};\n\n\tconst hasValue = Boolean(value && value.length > 0);\n\tconst visibleError = hasValue ? error : undefined;\n\tconst { controlId, labelId, descriptionId, errorId, describedBy } = useInputFieldIds({\n\t\tid: props.id,\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description,\n\t\thasError: !!visibleError\n\t});\n\tconst isInvalid = hasValue && (!!error || internalInvalid);\n\tconst resolvedEndAdornment = createEndAdornment({ endAdornment, onClear, disabled: disabled || !hasValue });\n\tconst hasCustomAdornment = endAdornment !== undefined && endAdornment !== null;\n\tconst resolvedEndAdornmentWidth = resolveEndAdornmentWidth(hasCustomAdornment, !!onClear, endAdornmentWidth);\n\n\treturn (\n\t\t<InputUI\n\t\t\tlabel={label}\n\t\t\tdescription={description}\n\t\t\tdisabled={disabled}\n\t\t\trequired={props.required}\n\t\t\tclassName={className}\n\t\t\tsize={size}\n\t\t\terror={visibleError}\n\t\t\tcontrolId={controlId}\n\t\t\tlabelId={labelId}\n\t\t\tdescriptionId={descriptionId}\n\t\t\terrorId={errorId}>\n\t\t\t<InputControl\n\t\t\t\tendAdornment={resolvedEndAdornment}\n\t\t\t\tendAdornmentClassName={endAdornmentClassName}\n\t\t\t\tendAdornmentWidth={resolvedEndAdornmentWidth}>\n\t\t\t\t{({ controlClassName }) => (\n\t\t\t\t\t<input\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t\tid={controlId}\n\t\t\t\t\t\ttype={type}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\taria-invalid={isInvalid || undefined}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\taria-describedby={describedBy}\n\t\t\t\t\t\tdata-invalid={isInvalid ? \"\" : undefined}\n\t\t\t\t\t\tclassName={cn(uiStyles.uiInputControl, styles.input, controlClassName)}\n\t\t\t\t\t\tonChange={handleInput}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</InputControl>\n\t\t</InputUI>\n\t);\n}\n\n/**\n * Числовое поле ввода с нормализацией значения и защитой от промежуточного невалидного ввода.\n */\nexport function InputNumber({\n\tlabel,\n\tdescription,\n\tdisabled,\n\tclassName,\n\tonClear,\n\terror,\n\tonClearError,\n\tonChange,\n\tvalue,\n\tsize,\n\tendAdornment,\n\tendAdornmentClassName,\n\tendAdornmentWidth,\n\t...props\n}: BaseInputProps<number | undefined>) {\n\tconst inputProps = props as InputHTMLAttributes<HTMLInputElement>;\n\tconst { controlId, labelId, descriptionId, errorId, describedBy } = useInputFieldIds({\n\t\tid: props.id,\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description,\n\t\thasError: !!error\n\t});\n\n\tconst inputRef = useRef<HTMLInputElement | null>(null);\n\tconst numberDraftRef = useRef<{ readonly value: number | undefined; readonly raw: string } | null>(null);\n\tconst externalRawValue = value !== undefined && typeof value === \"number\" && !isNaN(value) ? String(value) : \"\";\n\n\t/**\n\t * Числовой input хранит промежуточную строку в DOM, а controlled number остаётся\n\t * источником истины. Layout-effect синхронизирует именно внешнюю DOM-систему,\n\t * поэтому не создаёт каскадного React render и не ломает ввод неполных чисел.\n\t */\n\tuseLayoutEffect(() => {\n\t\tconst numberDraft = numberDraftRef.current;\n\t\tif (numberDraft && Object.is(numberDraft.value, value)) {\n\t\t\tif (inputRef.current && inputRef.current.value !== numberDraft.raw) {\n\t\t\t\tinputRef.current.value = numberDraft.raw;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tnumberDraftRef.current = null;\n\t\tif (inputRef.current && inputRef.current.value !== externalRawValue) {\n\t\t\tinputRef.current.value = externalRawValue;\n\t\t}\n\t}, [externalRawValue, value]);\n\n\tconst handleClear = onClear\n\t\t? () => {\n\t\t\t\tonClear();\n\t\t\t}\n\t\t: undefined;\n\n\tconst handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst raw = e.target.value;\n\n\t\tconst parsed = toFiniteNumber(raw); // ?? value ?? 0;\n\t\tnumberDraftRef.current = { value: parsed, raw };\n\n\t\tonChange(parsed);\n\t\tif (error && onClearError) onClearError();\n\t};\n\n\tconst resolvedEndAdornment = createEndAdornment({ endAdornment, onClear: handleClear, disabled });\n\tconst hasCustomAdornment = endAdornment !== undefined && endAdornment !== null;\n\tconst resolvedEndAdornmentWidth = resolveEndAdornmentWidth(hasCustomAdornment, !!handleClear, endAdornmentWidth);\n\n\treturn (\n\t\t<InputUI\n\t\t\tlabel={label}\n\t\t\tdescription={description}\n\t\t\tdisabled={disabled}\n\t\t\trequired={props.required}\n\t\t\tclassName={className}\n\t\t\tsize={size}\n\t\t\terror={error}\n\t\t\tcontrolId={controlId}\n\t\t\tlabelId={labelId}\n\t\t\tdescriptionId={descriptionId}\n\t\t\terrorId={errorId}>\n\t\t\t<InputControl\n\t\t\t\tendAdornment={resolvedEndAdornment}\n\t\t\t\tendAdornmentClassName={endAdornmentClassName}\n\t\t\t\tendAdornmentWidth={resolvedEndAdornmentWidth}>\n\t\t\t\t{({ controlClassName }) => (\n\t\t\t\t\t<input\n\t\t\t\t\t\t{...inputProps}\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\tid={controlId}\n\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\tinputMode=\"decimal\"\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tdefaultValue={externalRawValue}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\taria-describedby={describedBy}\n\t\t\t\t\t\tclassName={cn(uiStyles.uiInputControl, styles.input, styles.inputNumber, controlClassName)}\n\t\t\t\t\t\tonChange={handleInput}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</InputControl>\n\t\t</InputUI>\n\t);\n}\n\n/**\n * Совместимый алиас для `InputText`.\n */\nexport const Input = InputText;\n","import styles from \"../ui.module.scss\";\n\ninterface InputErrorComponentProps {\n\ttext: string;\n}\n\nexport function InputErrorComponent({ text }: InputErrorComponentProps) {\n\treturn <div className={styles.uiErrorElement}>{text}</div>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"input-VC8jEoTa.js","names":[],"sources":["../../src/input/Input.module.scss","../../src/input/lib/useInputFieldIds.ts","../../src/input/Input.tsx","../../src/input/InputErrorComponent.tsx"],"sourcesContent":["@use \"@/styles/mixins\" as *;\n\n.controlLayout {\n\tposition: relative;\n\twidth: 100%;\n}\n\n.input {\n\theight: var(--control-height);\n\twidth: 100%;\n\tpadding: var(--space-xs) var(--space-sm);\n\tfont-size: var(--font-size-md);\n\n\t--ui-color: var(--content-0);\n\t--ui-color-hover: var(--content-0);\n\t--ui-color-active: var(--content-0);\n\n\t--button-size: cacl(var(--control-height) - var(--border-width) * 2);\n}\n\n.inputNumber {\n\tappearance: textfield;\n\t-moz-appearance: textfield;\n\n\t&::-webkit-inner-spin-button,\n\t&::-webkit-outer-spin-button {\n\t\t-webkit-appearance: none;\n\t\tmargin: 0;\n\t}\n}\n\n.controlWithEndAdornment {\n\tpadding-right: calc(var(--input-end-adornment-width, var(--control-height)) + var(--space-sm));\n}\n\n.controlEndAdornment {\n\tposition: absolute;\n\theight: 100%;\n\ttop: 0;\n\tright: calc(var(--border-width) * 2);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n\tmax-width: calc(100% - var(--space-md));\n}\n\n.endAdornmentGroup {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: var(--space-xs);\n}\n\n.clearButton {\n\twidth: var(--button-size);\n\theight: var(--button-size);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder: none;\n\tborder-radius: var(--radius-sm);\n\n\tbackground: transparent;\n\tcolor: var(--content-2);\n\n\ttransition:\n\t\tcolor var(--transition-fast) ease,\n\t\tbackground-color var(--transition-fast) ease;\n\n\t&:hover {\n\t\tcolor: var(--content-accent);\n\t}\n\n\t&:focus-visible {\n\t\t@include focusVisible;\n\t}\n\n\t&:disabled {\n\t\tpointer-events: none;\n\t\topacity: var(--disabled-opacity);\n\t}\n}\n\n.error {\n\tposition: absolute;\n\tz-index: 11;\n\tbottom: calc(var(--space-xs) * -1);\n\tleft: 0;\n\tright: 0;\n\ttransform: translateY(100%);\n\t// font-size: var(--font-size-sm);\n\tcolor: var(--status-error-on-fill);\n\tbackground: var(--status-error-fill);\n\tborder-radius: var(--radius-sm);\n\tpadding: var(--space-xs) var(--space-sm);\n\tbox-shadow: var(--shadow-sm);\n\topacity: 0.9;\n}\n","import { useId } from \"react\";\n\nexport interface UseInputFieldIdsOptions {\n\tid?: string;\n\thasLabel?: boolean;\n\thasDescription?: boolean;\n\thasError?: boolean;\n}\n\nexport interface InputFieldIds {\n\tcontrolId: string;\n\tlabelId?: string;\n\tdescriptionId?: string;\n\terrorId?: string;\n\tdescribedBy?: string;\n}\n\nexport function useInputFieldIds({ id: externalId, hasLabel, hasDescription, hasError }: UseInputFieldIdsOptions): InputFieldIds {\n\tconst autoId = useId();\n\tconst controlId = externalId ?? autoId;\n\tconst labelId = hasLabel ? `${controlId}-label` : undefined;\n\tconst descriptionId = hasDescription ? `${controlId}-description` : undefined;\n\tconst errorId = hasError ? `${controlId}-error` : undefined;\n\tconst describedBy = [descriptionId, errorId].filter(Boolean).join(\" \") || undefined;\n\n\treturn {\n\t\tcontrolId,\n\t\tlabelId,\n\t\tdescriptionId,\n\t\terrorId,\n\t\tdescribedBy\n\t};\n}\n","import React, { CSSProperties, InputHTMLAttributes, ReactNode, useLayoutEffect, useRef, useState } from \"react\";\n\nimport { toFiniteNumber } from \"@ryuzaki13/react-foundation-lib/formatters\";\nimport { cn } from \"@ryuzaki13/react-foundation-lib/utils\";\nimport { XIcon } from \"lucide-react\";\n\nimport { UiBaseProps, UiSize } from \"../types\";\nimport uiStyles from \"../ui.module.scss\";\n\nimport styles from \"./Input.module.scss\";\nimport { useInputFieldIds } from \"./lib/useInputFieldIds\";\n\nexport interface InputUIProps extends Omit<UiBaseProps<never>, \"value\" | \"onChange\"> {\n\tclassName?: string;\n\terror?: string;\n\tcontrolId?: string;\n\tlabelId?: string;\n\tdescriptionId?: string;\n\terrorId?: string;\n\trequired?: boolean;\n\tchildren: React.ReactNode;\n}\n\nexport interface InputControlRenderProps {\n\tcontrolClassName?: string;\n}\n\nexport interface InputControlProps {\n\tclassName?: string;\n\tendAdornment?: ReactNode;\n\tendAdornmentClassName?: string;\n\tendAdornmentWidth?: string;\n\tchildren: (props: InputControlRenderProps) => ReactNode;\n}\n\nexport interface InputClearButtonProps {\n\tonClick?: () => void;\n\tdisabled?: boolean;\n\tclassName?: string;\n\tariaLabel?: string;\n}\n\nfunction InputLabel({ label, controlId, labelId }: { label: React.ReactNode; controlId?: string; labelId?: string }) {\n\treturn (\n\t\t<label id={labelId} htmlFor={controlId} className={uiStyles.uiLabel}>\n\t\t\t{label}\n\t\t</label>\n\t);\n}\n\nexport function InputUILoading({\n\tlabel,\n\tdescription,\n\tsize,\n\tdisabled,\n\tclassName\n}: Pick<InputUIProps, \"label\" | \"description\" | \"size\" | \"disabled\" | \"className\">) {\n\tconst { controlId, labelId, descriptionId } = useInputFieldIds({\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description\n\t});\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(uiStyles.uiElement, uiStyles.uiSizable, size && uiStyles[size as UiSize], uiStyles.disabled, className)}\n\t\t\taria-disabled={disabled || undefined}>\n\t\t\t{label && <InputLabel label={label} controlId={controlId} labelId={labelId} />}\n\t\t\t{description && (\n\t\t\t\t<p id={descriptionId} className={uiStyles.uiDescription}>\n\t\t\t\t\t{description}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<div className={cn(uiStyles.uiInputControlFake, \"skeletonLine\")} />\n\t\t</div>\n\t);\n}\n\n/**\n * Низкоуровневая оболочка поля ввода с общими элементами оформления.\n */\nexport function InputUI({\n\tlabel,\n\tdescription,\n\tdisabled,\n\tclassName,\n\terror,\n\tsize,\n\tcontrolId,\n\tlabelId,\n\tdescriptionId,\n\terrorId,\n\trequired,\n\tchildren\n}: InputUIProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\tuiStyles.uiElement,\n\t\t\t\tuiStyles.uiSizable,\n\t\t\t\tsize && uiStyles[size as UiSize],\n\t\t\t\tdisabled && uiStyles.disabled,\n\t\t\t\trequired && uiStyles.required,\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\taria-disabled={disabled || undefined}>\n\t\t\t{label && <InputLabel label={label} controlId={controlId} labelId={labelId} />}\n\t\t\t{description && (\n\t\t\t\t<p id={descriptionId} className={uiStyles.uiDescription}>\n\t\t\t\t\t{description}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<div className={styles.controlLayout}>\n\t\t\t\t{children}\n\n\t\t\t\t{error && (\n\t\t\t\t\t<p id={errorId} className={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport function InputControl({ className, endAdornment, endAdornmentClassName, endAdornmentWidth, children }: InputControlProps) {\n\tconst hasEndAdornment = endAdornment !== undefined && endAdornment !== null;\n\tconst style =\n\t\thasEndAdornment && endAdornmentWidth ? ({ \"--input-end-adornment-width\": endAdornmentWidth } as CSSProperties) : undefined;\n\n\treturn (\n\t\t<div className={cn(styles.controlLayout, className)} style={style}>\n\t\t\t{children({ controlClassName: hasEndAdornment ? styles.controlWithEndAdornment : undefined })}\n\t\t\t{hasEndAdornment && <div className={cn(styles.controlEndAdornment, endAdornmentClassName)}>{endAdornment}</div>}\n\t\t</div>\n\t);\n}\n\nexport function InputClearButton({ onClick, disabled, className, ariaLabel = \"Очистить значение\" }: InputClearButtonProps) {\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tdisabled={disabled || !onClick}\n\t\t\tclassName={cn(styles.clearButton, className)}\n\t\t\tonClick={onClick}\n\t\t\taria-label={ariaLabel}>\n\t\t\t<XIcon />\n\t\t</button>\n\t);\n}\n\nfunction resolveEndAdornmentWidth(hasAdornment: boolean, hasClearAction: boolean, explicitWidth?: string) {\n\tif (explicitWidth) {\n\t\treturn explicitWidth;\n\t}\n\n\tif (hasAdornment && hasClearAction) {\n\t\treturn \"calc((var(--control-height) * 2) + var(--space-xs))\";\n\t}\n\n\tif (hasAdornment || hasClearAction) {\n\t\treturn \"var(--control-height)\";\n\t}\n\n\treturn undefined;\n}\n\nfunction createEndAdornment({ endAdornment, onClear, disabled }: { endAdornment?: ReactNode; onClear?: () => void; disabled?: boolean }) {\n\tif ((endAdornment === undefined || endAdornment === null) && !onClear) {\n\t\treturn undefined;\n\t}\n\n\treturn (\n\t\t<div className={styles.endAdornmentGroup}>\n\t\t\t{endAdornment}\n\t\t\t{onClear && <InputClearButton onClick={onClear} disabled={disabled} />}\n\t\t</div>\n\t);\n}\n\nexport interface BaseInputProps<T extends number | string | undefined, V = T>\n\textends Omit<InputHTMLAttributes<HTMLInputElement>, \"size\" | \"value\" | \"type\" | \"defaultValue\" | \"onChange\">, UiBaseProps<T, V> {\n\tonClear?: () => void;\n\terror?: string;\n\tonClearError?: () => void;\n\tdefaultValue?: T;\n\tendAdornment?: ReactNode;\n\tendAdornmentClassName?: string;\n\tendAdornmentWidth?: string;\n}\n\nexport interface InputTextProps extends BaseInputProps<string> {\n\ttype?: \"email\" | \"password\" | \"search\" | \"tel\" | \"text\" | \"url\";\n\n\t/**\n\t * Регулярное выражение для валидации всего введённого значения.\n\t * При несоответствии поле подсвечивается как невалидное, но текст не удаляется.\n\t * Пример: `/^\\d*$/`\n\t */\n\tallowedPattern?: RegExp;\n}\n\n/**\n * Базовое текстовое поле ввода для строковых значений.\n */\nexport function InputText({\n\tlabel,\n\tdescription,\n\tdisabled,\n\tclassName,\n\tonClear,\n\terror,\n\tonClearError,\n\tonChange,\n\tvalue,\n\tsize,\n\tallowedPattern,\n\tendAdornment,\n\tendAdornmentClassName,\n\tendAdornmentWidth,\n\ttype = \"text\",\n\t...props\n}: InputTextProps) {\n\tconst [internalInvalid, setInternalInvalid] = useState(false);\n\n\tconst handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst newValue = e.target.value;\n\n\t\tif (allowedPattern !== undefined) {\n\t\t\tsetInternalInvalid(newValue !== \"\" && !allowedPattern.test(newValue));\n\t\t}\n\n\t\tonChange(newValue);\n\t\tif (error && onClearError) onClearError();\n\t};\n\n\tconst hasValue = Boolean(value && value.length > 0);\n\tconst visibleError = hasValue ? error : undefined;\n\tconst { controlId, labelId, descriptionId, errorId, describedBy } = useInputFieldIds({\n\t\tid: props.id,\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description,\n\t\thasError: !!visibleError\n\t});\n\tconst isInvalid = hasValue && (!!error || internalInvalid);\n\tconst resolvedEndAdornment = createEndAdornment({ endAdornment, onClear, disabled: disabled || !hasValue });\n\tconst hasCustomAdornment = endAdornment !== undefined && endAdornment !== null;\n\tconst resolvedEndAdornmentWidth = resolveEndAdornmentWidth(hasCustomAdornment, !!onClear, endAdornmentWidth);\n\n\treturn (\n\t\t<InputUI\n\t\t\tlabel={label}\n\t\t\tdescription={description}\n\t\t\tdisabled={disabled}\n\t\t\trequired={props.required}\n\t\t\tclassName={className}\n\t\t\tsize={size}\n\t\t\terror={visibleError}\n\t\t\tcontrolId={controlId}\n\t\t\tlabelId={labelId}\n\t\t\tdescriptionId={descriptionId}\n\t\t\terrorId={errorId}>\n\t\t\t<InputControl\n\t\t\t\tendAdornment={resolvedEndAdornment}\n\t\t\t\tendAdornmentClassName={endAdornmentClassName}\n\t\t\t\tendAdornmentWidth={resolvedEndAdornmentWidth}>\n\t\t\t\t{({ controlClassName }) => (\n\t\t\t\t\t<input\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t\tid={controlId}\n\t\t\t\t\t\ttype={type}\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\taria-invalid={isInvalid || undefined}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\taria-describedby={describedBy}\n\t\t\t\t\t\tdata-invalid={isInvalid ? \"\" : undefined}\n\t\t\t\t\t\tclassName={cn(uiStyles.uiInputControl, styles.input, controlClassName)}\n\t\t\t\t\t\tonChange={handleInput}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</InputControl>\n\t\t</InputUI>\n\t);\n}\n\n/**\n * Числовое поле ввода с нормализацией значения и защитой от промежуточного невалидного ввода.\n */\nexport function InputNumber({\n\tlabel,\n\tdescription,\n\tdisabled,\n\tclassName,\n\tonClear,\n\terror,\n\tonClearError,\n\tonChange,\n\tvalue,\n\tsize,\n\tendAdornment,\n\tendAdornmentClassName,\n\tendAdornmentWidth,\n\t...props\n}: BaseInputProps<number | undefined>) {\n\tconst inputProps = props as InputHTMLAttributes<HTMLInputElement>;\n\tconst { controlId, labelId, descriptionId, errorId, describedBy } = useInputFieldIds({\n\t\tid: props.id,\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description,\n\t\thasError: !!error\n\t});\n\n\tconst inputRef = useRef<HTMLInputElement | null>(null);\n\tconst numberDraftRef = useRef<{ readonly value: number | undefined; readonly raw: string } | null>(null);\n\tconst externalRawValue = value !== undefined && typeof value === \"number\" && !isNaN(value) ? String(value) : \"\";\n\n\t/**\n\t * Числовой input хранит промежуточную строку в DOM, а controlled number остаётся\n\t * источником истины. Layout-effect синхронизирует именно внешнюю DOM-систему,\n\t * поэтому не создаёт каскадного React render и не ломает ввод неполных чисел.\n\t */\n\tuseLayoutEffect(() => {\n\t\tconst numberDraft = numberDraftRef.current;\n\t\tif (numberDraft && Object.is(numberDraft.value, value)) {\n\t\t\tif (inputRef.current && inputRef.current.value !== numberDraft.raw) {\n\t\t\t\tinputRef.current.value = numberDraft.raw;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tnumberDraftRef.current = null;\n\t\tif (inputRef.current && inputRef.current.value !== externalRawValue) {\n\t\t\tinputRef.current.value = externalRawValue;\n\t\t}\n\t}, [externalRawValue, value]);\n\n\tconst handleClear = onClear\n\t\t? () => {\n\t\t\t\tonClear();\n\t\t\t}\n\t\t: undefined;\n\n\tconst handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst raw = e.target.value;\n\n\t\tconst parsed = toFiniteNumber(raw); // ?? value ?? 0;\n\t\tnumberDraftRef.current = { value: parsed, raw };\n\n\t\tonChange(parsed);\n\t\tif (error && onClearError) onClearError();\n\t};\n\n\tconst resolvedEndAdornment = createEndAdornment({ endAdornment, onClear: handleClear, disabled });\n\tconst hasCustomAdornment = endAdornment !== undefined && endAdornment !== null;\n\tconst resolvedEndAdornmentWidth = resolveEndAdornmentWidth(hasCustomAdornment, !!handleClear, endAdornmentWidth);\n\n\treturn (\n\t\t<InputUI\n\t\t\tlabel={label}\n\t\t\tdescription={description}\n\t\t\tdisabled={disabled}\n\t\t\trequired={props.required}\n\t\t\tclassName={className}\n\t\t\tsize={size}\n\t\t\terror={error}\n\t\t\tcontrolId={controlId}\n\t\t\tlabelId={labelId}\n\t\t\tdescriptionId={descriptionId}\n\t\t\terrorId={errorId}>\n\t\t\t<InputControl\n\t\t\t\tendAdornment={resolvedEndAdornment}\n\t\t\t\tendAdornmentClassName={endAdornmentClassName}\n\t\t\t\tendAdornmentWidth={resolvedEndAdornmentWidth}>\n\t\t\t\t{({ controlClassName }) => (\n\t\t\t\t\t<input\n\t\t\t\t\t\t{...inputProps}\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\tid={controlId}\n\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\tinputMode=\"decimal\"\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\tdefaultValue={externalRawValue}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\taria-describedby={describedBy}\n\t\t\t\t\t\tclassName={cn(uiStyles.uiInputControl, styles.input, styles.inputNumber, controlClassName)}\n\t\t\t\t\t\tonChange={handleInput}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</InputControl>\n\t\t</InputUI>\n\t);\n}\n\n/**\n * Совместимый алиас для `InputText`.\n */\nexport const Input = InputText;\n","import styles from \"../ui.module.scss\";\n\ninterface InputErrorComponentProps {\n\ttext: string;\n}\n\nexport function InputErrorComponent({ text }: InputErrorComponentProps) {\n\treturn <div className={styles.uiErrorElement}>{text}</div>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;ACiBA,SAAgB,EAAiB,EAAE,IAAI,GAAY,aAAU,mBAAgB,eAAoD;CAChI,IAAM,IAAS,EAAM,GACf,IAAY,KAAc,GAC1B,IAAU,IAAW,GAAG,EAAU,UAAU,KAAA,GAC5C,IAAgB,IAAiB,GAAG,EAAU,gBAAgB,KAAA,GAC9D,IAAU,IAAW,GAAG,EAAU,UAAU,KAAA;CAGlD,OAAO;EACN;EACA;EACA;EACA;EACA,aAPmB,CAAC,GAAe,CAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA;CAQ1E;AACD;;;ACUA,SAAS,EAAW,EAAE,UAAO,cAAW,cAA6E;CACpH,OACC,kBAAC,SAAD;EAAO,IAAI;EAAS,SAAS;EAAW,WAAW,EAAS;EAC1D,UAAA;CACK,CAAA;AAET;AAEA,SAAgB,EAAe,EAC9B,UACA,gBACA,SACA,aACA,gBACmF;CACnF,IAAM,EAAE,cAAW,YAAS,qBAAkB,EAAiB;EAC9D,UAAU,KAAiC;EAC3C,gBAAgB,CAAC,CAAC;CACnB,CAAC;CAED,OACC,kBAAC,OAAD;EACC,WAAW,EAAG,EAAS,WAAW,EAAS,WAAW,KAAQ,EAAS,IAAiB,EAAS,UAAU,CAAS;EACpH,iBAAe,KAAY,KAAA;EAF5B,UAAA;GAGE,KAAS,kBAAC,GAAD;IAAmB;IAAkB;IAAoB;GAAU,CAAA;GAC5E,KACA,kBAAC,KAAD;IAAG,IAAI;IAAe,WAAW,EAAS;IACxC,UAAA;GACC,CAAA;GAEJ,kBAAC,OAAD,EAAK,WAAW,EAAG,EAAS,oBAAoB,cAAc,EAAI,CAAA;EAC9D;;AAEP;AAKA,SAAgB,EAAQ,EACvB,UACA,gBACA,aACA,cACA,UACA,SACA,cACA,YACA,kBACA,YACA,aACA,eACgB;CAChB,OACC,kBAAC,OAAD;EACC,WAAW,EACV,EAAS,WACT,EAAS,WACT,KAAQ,EAAS,IACjB,KAAY,EAAS,UACrB,KAAY,EAAS,UACrB,CACD;EACA,iBAAe,KAAY,KAAA;EAT5B,UAAA;GAUE,KAAS,kBAAC,GAAD;IAAmB;IAAkB;IAAoB;GAAU,CAAA;GAC5E,KACA,kBAAC,KAAD;IAAG,IAAI;IAAe,WAAW,EAAS;IACxC,UAAA;GACC,CAAA;GAEJ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAvB,UAAA,CACE,GAEA,KACA,kBAAC,KAAD;KAAG,IAAI;KAAS,WAAW,EAAO;KAAO,MAAK;KAC5C,UAAA;IACC,CAAA,CAEA;;EACD;;AAEP;AAEA,SAAgB,EAAa,EAAE,cAAW,iBAAc,0BAAuB,sBAAmB,eAA+B;CAChI,IAAM,IAAkB,KAA+C,MACjE,IACL,KAAmB,IAAqB,EAAE,+BAA+B,EAAkB,IAAsB,KAAA;CAElH,OACC,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,eAAe,CAAS;EAAU;EAA5D,UAAA,CACE,EAAS,EAAE,kBAAkB,IAAkB,EAAO,0BAA0B,KAAA,EAAU,CAAC,GAC3F,KAAmB,kBAAC,OAAD;GAAK,WAAW,EAAG,EAAO,qBAAqB,CAAqB;GAAI,UAAA;EAAkB,CAAA,CAC1G;;AAEP;AAEA,SAAgB,EAAiB,EAAE,YAAS,aAAU,cAAW,eAAY,uBAA8C;CAC1H,OACC,kBAAC,UAAD;EACC,MAAK;EACL,UAAU,KAAY,CAAC;EACvB,WAAW,EAAG,EAAO,aAAa,CAAS;EAClC;EACT,cAAY;EACZ,UAAA,kBAAC,GAAD,CAAQ,CAAA;CACD,CAAA;AAEV;AAEA,SAAS,EAAyB,GAAuB,GAAyB,GAAwB;CACzG,IAAI,GACH,OAAO;CAGR,IAAI,KAAgB,GACnB,OAAO;CAGR,IAAI,KAAgB,GACnB,OAAO;AAIT;AAEA,SAAS,EAAmB,EAAE,iBAAc,YAAS,eAAoF;CACxI,MAAK,KAA+C,QAAS,CAAC,IAI9D,OACC,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAvB,UAAA,CACE,GACA,KAAW,kBAAC,GAAD;GAAkB,SAAS;GAAmB;EAAW,CAAA,CACjE;;AAEP;AA2BA,SAAgB,EAAU,EACzB,UACA,gBACA,aACA,cACA,YACA,UACA,iBACA,aACA,UACA,SACA,mBACA,iBACA,0BACA,sBACA,UAAO,QACP,GAAG,KACe;CAClB,IAAM,CAAC,GAAiB,KAAsB,EAAS,EAAK,GAEtD,KAAe,MAA2C;EAC/D,IAAM,IAAW,EAAE,OAAO;EAO1B,AALI,MAAmB,KAAA,KACtB,EAAmB,MAAa,MAAM,CAAC,EAAe,KAAK,CAAQ,CAAC,GAGrE,EAAS,CAAQ,GACb,KAAS,KAAc,EAAa;CACzC,GAEM,IAAW,GAAQ,KAAS,EAAM,SAAS,IAC3C,IAAe,IAAW,IAAQ,KAAA,GAClC,EAAE,cAAW,YAAS,kBAAe,YAAS,mBAAgB,EAAiB;EACpF,IAAI,EAAM;EACV,UAAU,KAAiC;EAC3C,gBAAgB,CAAC,CAAC;EAClB,UAAU,CAAC,CAAC;CACb,CAAC,GACK,IAAY,MAAa,CAAC,CAAC,KAAS,IACpC,IAAuB,EAAmB;EAAE;EAAc;EAAS,UAAU,KAAY,CAAC;CAAS,CAAC,GAEpG,IAA4B,EADP,KAA+C,MACK,CAAC,CAAC,GAAS,CAAiB;CAE3G,OACC,kBAAC,GAAD;EACQ;EACM;EACH;EACV,UAAU,EAAM;EACL;EACL;EACN,OAAO;EACI;EACF;EACM;EACN;EACT,UAAA,kBAAC,GAAD;GACC,cAAc;GACS;GACvB,mBAAmB;GACjB,WAAA,EAAE,0BACH,kBAAC,SAAD;IACC,GAAI;IACJ,IAAI;IACE;IACI;IACH;IACP,gBAAc,KAAa,KAAA;IAC3B,mBAAiB;IACjB,oBAAkB;IAClB,gBAAc,IAAY,KAAK,KAAA;IAC/B,WAAW,EAAG,EAAS,gBAAgB,EAAO,OAAO,CAAgB;IACrE,UAAU;GACV,CAAA;EAEW,CAAA;CACN,CAAA;AAEX;AAKA,SAAgB,EAAY,EAC3B,UACA,gBACA,aACA,cACA,YACA,UACA,iBACA,aACA,UACA,SACA,iBACA,0BACA,sBACA,GAAG,KACmC;CACtC,IAAM,IAAa,GACb,EAAE,cAAW,YAAS,kBAAe,YAAS,mBAAgB,EAAiB;EACpF,IAAI,EAAM;EACV,UAAU,KAAiC;EAC3C,gBAAgB,CAAC,CAAC;EAClB,UAAU,CAAC,CAAC;CACb,CAAC,GAEK,IAAW,EAAgC,IAAI,GAC/C,IAAiB,EAA4E,IAAI,GACjG,IAAmB,MAAU,KAAA,KAAa,OAAO,KAAU,YAAY,CAAC,MAAM,CAAK,IAAI,OAAO,CAAK,IAAI;CAO7G,QAAsB;EACrB,IAAM,IAAc,EAAe;EACnC,IAAI,KAAe,OAAO,GAAG,EAAY,OAAO,CAAK,GAAG;GACvD,AAAI,EAAS,WAAW,EAAS,QAAQ,UAAU,EAAY,QAC9D,EAAS,QAAQ,QAAQ,EAAY;GAEtC;EACD;EAGA,AADA,EAAe,UAAU,MACrB,EAAS,WAAW,EAAS,QAAQ,UAAU,MAClD,EAAS,QAAQ,QAAQ;CAE3B,GAAG,CAAC,GAAkB,CAAK,CAAC;CAE5B,IAAM,IAAc,UACX;EACN,EAAQ;CACT,IACC,KAAA,GAEG,KAAe,MAA2C;EAC/D,IAAM,IAAM,EAAE,OAAO,OAEf,IAAS,EAAe,CAAG;EAIjC,AAHA,EAAe,UAAU;GAAE,OAAO;GAAQ;EAAI,GAE9C,EAAS,CAAM,GACX,KAAS,KAAc,EAAa;CACzC,GAEM,IAAuB,EAAmB;EAAE;EAAc,SAAS;EAAa;CAAS,CAAC,GAE1F,IAA4B,EADP,KAA+C,MACK,CAAC,CAAC,GAAa,CAAiB;CAE/G,OACC,kBAAC,GAAD;EACQ;EACM;EACH;EACV,UAAU,EAAM;EACL;EACL;EACC;EACI;EACF;EACM;EACN;EACT,UAAA,kBAAC,GAAD;GACC,cAAc;GACS;GACvB,mBAAmB;GACjB,WAAA,EAAE,0BACH,kBAAC,SAAD;IACC,GAAI;IACJ,KAAK;IACL,IAAI;IACJ,MAAK;IACL,WAAU;IACA;IACV,cAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,WAAW,EAAG,EAAS,gBAAgB,EAAO,OAAO,EAAO,aAAa,CAAgB;IACzF,UAAU;GACV,CAAA;EAEW,CAAA;CACN,CAAA;AAEX;AAKA,IAAa,IAAQ;;;ACtYrB,SAAgB,EAAoB,EAAE,WAAkC;CACvE,OAAO,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAiB,UAAA;CAAU,CAAA;AAC1D"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as e } from "./ui.module-DHVTOqZ0.js";
|
|
2
|
-
import { i as t, l as n, s as r } from "./input-
|
|
2
|
+
import { i as t, l as n, s as r } from "./input-VC8jEoTa.js";
|
|
3
3
|
import { useCallback as i, useRef as a } from "react";
|
|
4
4
|
import { cn as o, handleKeyboardActivation as s } from "@ryuzaki13/react-foundation-lib/utils";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
|
+
import { XIcon as u } from "lucide-react";
|
|
7
7
|
import { ReadFileError as d, assertValidAccept as f, assertValidAllowedMime as p, readFile as m } from "@ryuzaki13/react-foundation-lib/file";
|
|
8
8
|
var h = {
|
|
9
9
|
control: "h9SMea",
|
|
@@ -19,27 +19,27 @@ function g({ files: e, onChange: t }) {
|
|
|
19
19
|
let n = i((n) => {
|
|
20
20
|
t(e.filter((e, t) => t !== n));
|
|
21
21
|
}, [e, t]);
|
|
22
|
-
return !e || !e.length ? null : /* @__PURE__ */
|
|
22
|
+
return !e || !e.length ? null : /* @__PURE__ */ c("ul", {
|
|
23
23
|
className: h.fileList,
|
|
24
|
-
children: e.map((e, t) => /* @__PURE__ */
|
|
24
|
+
children: e.map((e, t) => /* @__PURE__ */ l("li", {
|
|
25
25
|
className: h.fileItem,
|
|
26
|
-
children: [/* @__PURE__ */
|
|
26
|
+
children: [/* @__PURE__ */ c("span", {
|
|
27
27
|
className: h.fileItemName,
|
|
28
28
|
children: e.meta.name
|
|
29
|
-
}), /* @__PURE__ */
|
|
29
|
+
}), /* @__PURE__ */ c("button", {
|
|
30
30
|
type: "button",
|
|
31
31
|
className: o(h.removeButton, "flexCenter"),
|
|
32
32
|
onClick: () => n(t),
|
|
33
33
|
"aria-label": `Удалить ${e.meta.name}`,
|
|
34
|
-
children: /* @__PURE__ */
|
|
34
|
+
children: /* @__PURE__ */ c(u, {})
|
|
35
35
|
})]
|
|
36
36
|
}, `${e.meta.name}-${e.meta.lastModified}`))
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region src/input-files/InputFiles.tsx
|
|
41
|
-
function _(
|
|
42
|
-
let { value: _, onReadError: v, accept: y, allowedMime: b, maxBytes: x, maxFiles: S, label: C, description: w, placeholder: T = "Выберите файлы", disabled: E, size: D, error: O, onClearError: k, className: A } =
|
|
41
|
+
function _(u) {
|
|
42
|
+
let { value: _, onReadError: v, accept: y, allowedMime: b, maxBytes: x, maxFiles: S, label: C, description: w, placeholder: T = "Выберите файлы", disabled: E, size: D, error: O, onClearError: k, className: A } = u;
|
|
43
43
|
p(b), f(y);
|
|
44
44
|
let j = a(null), { controlId: M, labelId: N, descriptionId: P, errorId: F, describedBy: I } = n({
|
|
45
45
|
hasLabel: C != null,
|
|
@@ -51,7 +51,7 @@ function _(c) {
|
|
|
51
51
|
let n = Array.from(t);
|
|
52
52
|
e.target.value = "", O && k && k();
|
|
53
53
|
let r = typeof S == "number" ? S - _.length : n.length, i = n.slice(0, r);
|
|
54
|
-
if (
|
|
54
|
+
if (u.readMode === "array-buffer") {
|
|
55
55
|
let e = [];
|
|
56
56
|
for (let t of i) try {
|
|
57
57
|
let n = await m(t, {
|
|
@@ -63,7 +63,7 @@ function _(c) {
|
|
|
63
63
|
} catch (e) {
|
|
64
64
|
e instanceof d && v && v(e);
|
|
65
65
|
}
|
|
66
|
-
e.length > 0 &&
|
|
66
|
+
e.length > 0 && u.onChange([...u.value, ...e]);
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
let a = [];
|
|
@@ -76,9 +76,9 @@ function _(c) {
|
|
|
76
76
|
} catch (e) {
|
|
77
77
|
e instanceof d && v && v(e);
|
|
78
78
|
}
|
|
79
|
-
a.length > 0 &&
|
|
79
|
+
a.length > 0 && u.onChange([...u.value, ...a]);
|
|
80
80
|
}, [
|
|
81
|
-
|
|
81
|
+
u,
|
|
82
82
|
S,
|
|
83
83
|
_.length,
|
|
84
84
|
b,
|
|
@@ -88,11 +88,11 @@ function _(c) {
|
|
|
88
88
|
k
|
|
89
89
|
]), z = i(() => {
|
|
90
90
|
j.current?.click();
|
|
91
|
-
}, []), B = (
|
|
92
|
-
files:
|
|
93
|
-
onChange:
|
|
91
|
+
}, []), B = (u.readMode, /* @__PURE__ */ c(g, {
|
|
92
|
+
files: u.value,
|
|
93
|
+
onChange: u.onChange
|
|
94
94
|
}));
|
|
95
|
-
return /* @__PURE__ */
|
|
95
|
+
return /* @__PURE__ */ l(r, {
|
|
96
96
|
label: C,
|
|
97
97
|
description: w,
|
|
98
98
|
disabled: E,
|
|
@@ -104,7 +104,7 @@ function _(c) {
|
|
|
104
104
|
descriptionId: P,
|
|
105
105
|
errorId: F,
|
|
106
106
|
children: [
|
|
107
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ c("input", {
|
|
108
108
|
ref: j,
|
|
109
109
|
id: M,
|
|
110
110
|
type: "file",
|
|
@@ -115,7 +115,7 @@ function _(c) {
|
|
|
115
115
|
onChange: R,
|
|
116
116
|
tabIndex: -1
|
|
117
117
|
}),
|
|
118
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ c(t, { children: () => /* @__PURE__ */ c("div", {
|
|
119
119
|
role: "button",
|
|
120
120
|
tabIndex: E || L ? -1 : 0,
|
|
121
121
|
onClick: z,
|
|
@@ -126,7 +126,7 @@ function _(c) {
|
|
|
126
126
|
"aria-disabled": E || L,
|
|
127
127
|
"aria-labelledby": N,
|
|
128
128
|
"aria-describedby": I,
|
|
129
|
-
children: /* @__PURE__ */
|
|
129
|
+
children: /* @__PURE__ */ c("span", {
|
|
130
130
|
className: e.uiPlaceholder,
|
|
131
131
|
children: L ? `Максимум ${S} файлов` : T
|
|
132
132
|
})
|
|
@@ -138,4 +138,4 @@ function _(c) {
|
|
|
138
138
|
//#endregion
|
|
139
139
|
export { g as n, _ as t };
|
|
140
140
|
|
|
141
|
-
//# sourceMappingURL=input-files-
|
|
141
|
+
//# sourceMappingURL=input-files-CoMjadk7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-files-DxLVr2PF.js","names":[],"sources":["../../src/input-files/InputFiles.module.scss","../../src/input-files/InputFilesList.tsx","../../src/input-files/InputFiles.tsx"],"sourcesContent":["/* Контрол, имитирующий внешний вид InputText */\n.control {\n\theight: var(--control-height);\n\twidth: 100%;\n\tpadding: 0 var(--space-sm);\n\tfont-size: var(--font-size-md);\n\tcursor: pointer;\n\n\tdisplay: flex;\n\talign-items: center;\n}\n\n/* Скрытый нативный input[type=\"file\"] */\n.hiddenInput {\n\tposition: absolute;\n\twidth: 0;\n\theight: 0;\n\topacity: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n}\n\n/* Список выбранных файлов */\n.fileList {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--space-xs);\n\tmargin-top: var(--space-xs);\n}\n\n/* Элемент списка — строка с именем файла и кнопкой удаления */\n.fileItem {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: var(--space-xs);\n\tpadding: var(--space-xs) var(--space-sm);\n\tbackground-color: var(--surface-2);\n\tborder-radius: var(--radius-sm);\n\t// font-size: var(--font-size-sm);\n}\n\n/* Имя файла — обрезка с троеточием */\n.fileItemName {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Кнопка удаления файла из списка */\n.removeButton {\n\tflex-shrink: 0;\n\twidth: 1.25em;\n\theight: 1.25em;\n\tcolor: var(--content-2);\n\n\t&:hover {\n\t\tcolor: var(--content-accent);\n\t}\n}\n","import { useCallback } from \"react\";\n\nimport { ReadFileResult } from \"@ryuzaki13/react-foundation-lib/file\";\nimport { cn } from \"@ryuzaki13/react-foundation-lib/utils\";\nimport { XIcon } from \"lucide-react\";\n\nimport styles from \"./InputFiles.module.scss\";\n\ninterface InputFilesListProps<TFile extends ReadFileResult> {\n\tfiles: TFile[];\n\tonChange: (files: TFile[]) => void;\n}\n\nexport function InputFilesList<TFile extends ReadFileResult>({ files, onChange }: InputFilesListProps<TFile>) {\n\tconst handleRemove = useCallback(\n\t\t(index: number) => {\n\t\t\tonChange(files.filter((_, i) => i !== index));\n\t\t},\n\t\t[files, onChange]\n\t);\n\n\tif (!files || !files.length) return null;\n\n\treturn (\n\t\t<ul className={styles.fileList}>\n\t\t\t{files.map((item, index) => (\n\t\t\t\t<li key={`${item.meta.name}-${item.meta.lastModified}`} className={styles.fileItem}>\n\t\t\t\t\t<span className={styles.fileItemName}>{item.meta.name}</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={cn(styles.removeButton, \"flexCenter\")}\n\t\t\t\t\t\tonClick={() => handleRemove(index)}\n\t\t\t\t\t\taria-label={`Удалить ${item.meta.name}`}>\n\t\t\t\t\t\t<XIcon />\n\t\t\t\t\t</button>\n\t\t\t\t</li>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\n","import { useCallback, useRef } from \"react\";\n\nimport { assertValidAccept, assertValidAllowedMime, readFile, ReadFileError, ReadFileResult } from \"@ryuzaki13/react-foundation-lib/file\";\nimport { cn, handleKeyboardActivation } from \"@ryuzaki13/react-foundation-lib/utils\";\n\nimport { InputControl, InputUI, useInputFieldIds } from \"../input\";\nimport { UiBaseProps } from \"../types\";\nimport uiStyles from \"../ui.module.scss\";\n\nimport styles from \"./InputFiles.module.scss\";\nimport { InputFilesList } from \"./InputFilesList\";\n\ninterface InputFilesCommonProps extends Omit<UiBaseProps<undefined>, \"value\" | \"onChange\"> {\n\t/** Callback ошибки чтения файла */\n\tonReadError?: (error: ReadFileError) => void;\n\n\t/** HTML-атрибут accept для нативного input[type=\"file\"] (например `[\".pdf\", \".docx\"]`) */\n\taccept?: readonly string[];\n\t/** Список допустимых MIME-типов — передаётся в readFile */\n\tallowedMime?: readonly string[];\n\t/** Максимальный размер одного файла в байтах */\n\tmaxBytes?: number;\n\t/** Максимальное количество файлов. При достижении лимита input блокируется */\n\tmaxFiles?: number;\n\n\t/** Текст ошибки под полем */\n\terror?: string;\n\t/** Callback сброса ошибки */\n\tonClearError?: () => void;\n\t/** CSS-класс обёртки */\n\tclassName?: string;\n}\n\ntype InputFilesDataUrlResult = Extract<ReadFileResult, { mode: \"data-url\" }>;\ntype InputFilesArrayBufferResult = Extract<ReadFileResult, { mode: \"array-buffer\" }>;\n\n/**\n * Пропсы компонента InputFiles.\n *\n * Компонент предназначен для выбора нескольких файлов.\n * Выбранные файлы отображаются списком под полем ввода,\n * каждый с кнопкой удаления (крестик).\n *\n * В onChange передаётся обновлённый массив результатов readFile.\n */\nexport type InputFilesProps =\n\t| (InputFilesCommonProps & {\n\t\t\tvalue: InputFilesDataUrlResult[];\n\t\t\tonChange: (value: InputFilesDataUrlResult[]) => void;\n\t\t\t/** Режим чтения: \"data-url\" по умолчанию */\n\t\t\treadMode?: \"data-url\";\n\t })\n\t| (InputFilesCommonProps & {\n\t\t\tvalue: InputFilesArrayBufferResult[];\n\t\t\tonChange: (value: InputFilesArrayBufferResult[]) => void;\n\t\t\t/** Режим чтения файла как ArrayBuffer */\n\t\t\treadMode: \"array-buffer\";\n\t });\n\n/**\n * InputFiles — компонент выбора нескольких файлов.\n *\n * Поле стилизовано как InputText. При клике открывается нативный\n * диалог с multiple. Каждый выбранный файл читается через readFile\n * и добавляется в массив value.\n *\n * Список выбранных файлов отображается под полем.\n * Каждый файл можно удалить кнопкой «крестик».\n *\n * InputFiles типизирует массив по `readMode`.\n * Если `readMode` не задан или равен `\"data-url\"`, в `value` и `onChange`\n * используется массив объектов с `dataUrl`. Если `readMode` равен\n * `\"array-buffer\"`, используется массив объектов с `buffer`.\n */\nexport function InputFiles(props: InputFilesProps) {\n\tconst {\n\t\tvalue,\n\t\tonReadError,\n\t\taccept,\n\t\tallowedMime,\n\t\tmaxBytes,\n\t\tmaxFiles,\n\t\tlabel,\n\t\tdescription,\n\t\tplaceholder = \"Выберите файлы\",\n\t\tdisabled,\n\t\tsize,\n\t\terror,\n\t\tonClearError,\n\t\tclassName\n\t} = props;\n\n\tassertValidAllowedMime(allowedMime);\n\tassertValidAccept(accept);\n\n\t/** Реф на скрытый нативный input[type=\"file\"] */\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst { controlId, labelId, descriptionId, errorId, describedBy } = useInputFieldIds({\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description,\n\t\thasError: !!error\n\t});\n\n\t/** Достигнут ли лимит файлов */\n\tconst isLimitReached = typeof maxFiles === \"number\" && value.length >= maxFiles;\n\n\t/**\n\t * Обработчик выбора файлов из нативного диалога.\n\t * Каждый файл читается через readFile.\n\t * Успешно прочитанные файлы добавляются к текущему массиву value.\n\t */\n\tconst handleChange = useCallback(\n\t\tasync (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\t\tconst files = e.target.files;\n\t\t\tif (!files || files.length === 0) return;\n\n\t\t\tconst selectedFiles = Array.from(files);\n\n\t\t\t// Сбрасываем значение input чтобы можно было выбрать те же файлы повторно\n\t\t\te.target.value = \"\";\n\n\t\t\t// Сбрасываем ошибку при выборе новых файлов\n\t\t\tif (error && onClearError) onClearError();\n\n\t\t\t// Определяем сколько файлов можно ещё добавить\n\t\t\tconst remaining = typeof maxFiles === \"number\" ? maxFiles - value.length : selectedFiles.length;\n\t\t\tconst filesToProcess = selectedFiles.slice(0, remaining);\n\n\t\t\tif (props.readMode === \"array-buffer\") {\n\t\t\t\t// Читаем файлы как ArrayBuffer и сохраняем точный тип массива для потребителя.\n\t\t\t\tconst results: InputFilesArrayBufferResult[] = [];\n\n\t\t\t\tfor (const file of filesToProcess) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await readFile(file, { allowedMime, maxBytes, mode: \"array-buffer\" });\n\t\t\t\t\t\tresults.push(result);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tif (err instanceof ReadFileError && onReadError) {\n\t\t\t\t\t\t\tonReadError(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (results.length > 0) {\n\t\t\t\t\tprops.onChange([...props.value, ...results]);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Читаем файлы как Data URL и сохраняем точный тип массива для потребителя.\n\t\t\tconst results: InputFilesDataUrlResult[] = [];\n\t\t\tfor (const file of filesToProcess) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await readFile(file, { allowedMime, maxBytes });\n\t\t\t\t\tresults.push(result);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (err instanceof ReadFileError && onReadError) {\n\t\t\t\t\t\tonReadError(err);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (results.length > 0) {\n\t\t\t\tprops.onChange([...props.value, ...results]);\n\t\t\t}\n\t\t},\n\t\t[props, maxFiles, value.length, allowedMime, maxBytes, onReadError, error, onClearError]\n\t);\n\n\t/** Клик по видимому контролу открывает нативный диалог */\n\tconst handleClick = useCallback(() => {\n\t\tinputRef.current?.click();\n\t}, []);\n\n\tconst filesList =\n\t\tprops.readMode === \"array-buffer\" ? (\n\t\t\t<InputFilesList files={props.value} onChange={props.onChange} />\n\t\t) : (\n\t\t\t<InputFilesList files={props.value} onChange={props.onChange} />\n\t\t);\n\n\treturn (\n\t\t<InputUI\n\t\t\tlabel={label}\n\t\t\tdescription={description}\n\t\t\tdisabled={disabled}\n\t\t\tclassName={className}\n\t\t\tsize={size}\n\t\t\terror={error}\n\t\t\tcontrolId={controlId}\n\t\t\tlabelId={labelId}\n\t\t\tdescriptionId={descriptionId}\n\t\t\terrorId={errorId}>\n\t\t\t<input\n\t\t\t\tref={inputRef}\n\t\t\t\tid={controlId}\n\t\t\t\ttype=\"file\"\n\t\t\t\taccept={(accept ?? allowedMime)?.join(\",\")}\n\t\t\t\tmultiple\n\t\t\t\tdisabled={disabled || isLimitReached}\n\t\t\t\tclassName={styles.hiddenInput}\n\t\t\t\tonChange={handleChange}\n\t\t\t\ttabIndex={-1}\n\t\t\t/>\n\n\t\t\t<InputControl>\n\t\t\t\t{() => (\n\t\t\t\t\t<div\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\ttabIndex={disabled || isLimitReached ? -1 : 0}\n\t\t\t\t\t\tonClick={handleClick}\n\t\t\t\t\t\tonKeyDown={(event) => handleKeyboardActivation(event, handleClick, { disabled: disabled || isLimitReached })}\n\t\t\t\t\t\tclassName={cn(uiStyles.uiInputControl, styles.control, !!error && \"invalid\")}\n\t\t\t\t\t\tdata-disabled={disabled || undefined}\n\t\t\t\t\t\taria-invalid={!!error || undefined}\n\t\t\t\t\t\taria-disabled={disabled || isLimitReached}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\taria-describedby={describedBy}>\n\t\t\t\t\t\t<span className={uiStyles.uiPlaceholder}>{isLimitReached ? `Максимум ${maxFiles} файлов` : placeholder}</span>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</InputControl>\n\n\t\t\t{/* Список выбранных файлов */}\n\t\t\t{filesList}\n\t\t</InputUI>\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"input-files-CoMjadk7.js","names":[],"sources":["../../src/input-files/InputFiles.module.scss","../../src/input-files/InputFilesList.tsx","../../src/input-files/InputFiles.tsx"],"sourcesContent":["/* Контрол, имитирующий внешний вид InputText */\n.control {\n\theight: var(--control-height);\n\twidth: 100%;\n\tpadding: 0 var(--space-sm);\n\tfont-size: var(--font-size-md);\n\tcursor: pointer;\n\n\tdisplay: flex;\n\talign-items: center;\n}\n\n/* Скрытый нативный input[type=\"file\"] */\n.hiddenInput {\n\tposition: absolute;\n\twidth: 0;\n\theight: 0;\n\topacity: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n}\n\n/* Список выбранных файлов */\n.fileList {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--space-xs);\n\tmargin-top: var(--space-xs);\n}\n\n/* Элемент списка — строка с именем файла и кнопкой удаления */\n.fileItem {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: var(--space-xs);\n\tpadding: var(--space-xs) var(--space-sm);\n\tbackground-color: var(--surface-2);\n\tborder-radius: var(--radius-sm);\n\t// font-size: var(--font-size-sm);\n}\n\n/* Имя файла — обрезка с троеточием */\n.fileItemName {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Кнопка удаления файла из списка */\n.removeButton {\n\tflex-shrink: 0;\n\twidth: 1.25em;\n\theight: 1.25em;\n\tcolor: var(--content-2);\n\n\t&:hover {\n\t\tcolor: var(--content-accent);\n\t}\n}\n","import { useCallback } from \"react\";\n\nimport { ReadFileResult } from \"@ryuzaki13/react-foundation-lib/file\";\nimport { cn } from \"@ryuzaki13/react-foundation-lib/utils\";\nimport { XIcon } from \"lucide-react\";\n\nimport styles from \"./InputFiles.module.scss\";\n\ninterface InputFilesListProps<TFile extends ReadFileResult> {\n\tfiles: TFile[];\n\tonChange: (files: TFile[]) => void;\n}\n\nexport function InputFilesList<TFile extends ReadFileResult>({ files, onChange }: InputFilesListProps<TFile>) {\n\tconst handleRemove = useCallback(\n\t\t(index: number) => {\n\t\t\tonChange(files.filter((_, i) => i !== index));\n\t\t},\n\t\t[files, onChange]\n\t);\n\n\tif (!files || !files.length) return null;\n\n\treturn (\n\t\t<ul className={styles.fileList}>\n\t\t\t{files.map((item, index) => (\n\t\t\t\t<li key={`${item.meta.name}-${item.meta.lastModified}`} className={styles.fileItem}>\n\t\t\t\t\t<span className={styles.fileItemName}>{item.meta.name}</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={cn(styles.removeButton, \"flexCenter\")}\n\t\t\t\t\t\tonClick={() => handleRemove(index)}\n\t\t\t\t\t\taria-label={`Удалить ${item.meta.name}`}>\n\t\t\t\t\t\t<XIcon />\n\t\t\t\t\t</button>\n\t\t\t\t</li>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\n","import { useCallback, useRef } from \"react\";\n\nimport { assertValidAccept, assertValidAllowedMime, readFile, ReadFileError, ReadFileResult } from \"@ryuzaki13/react-foundation-lib/file\";\nimport { cn, handleKeyboardActivation } from \"@ryuzaki13/react-foundation-lib/utils\";\n\nimport { InputControl, InputUI, useInputFieldIds } from \"../input\";\nimport { UiBaseProps } from \"../types\";\nimport uiStyles from \"../ui.module.scss\";\n\nimport styles from \"./InputFiles.module.scss\";\nimport { InputFilesList } from \"./InputFilesList\";\n\ninterface InputFilesCommonProps extends Omit<UiBaseProps<undefined>, \"value\" | \"onChange\"> {\n\t/** Callback ошибки чтения файла */\n\tonReadError?: (error: ReadFileError) => void;\n\n\t/** HTML-атрибут accept для нативного input[type=\"file\"] (например `[\".pdf\", \".docx\"]`) */\n\taccept?: readonly string[];\n\t/** Список допустимых MIME-типов — передаётся в readFile */\n\tallowedMime?: readonly string[];\n\t/** Максимальный размер одного файла в байтах */\n\tmaxBytes?: number;\n\t/** Максимальное количество файлов. При достижении лимита input блокируется */\n\tmaxFiles?: number;\n\n\t/** Текст ошибки под полем */\n\terror?: string;\n\t/** Callback сброса ошибки */\n\tonClearError?: () => void;\n\t/** CSS-класс обёртки */\n\tclassName?: string;\n}\n\ntype InputFilesDataUrlResult = Extract<ReadFileResult, { mode: \"data-url\" }>;\ntype InputFilesArrayBufferResult = Extract<ReadFileResult, { mode: \"array-buffer\" }>;\n\n/**\n * Пропсы компонента InputFiles.\n *\n * Компонент предназначен для выбора нескольких файлов.\n * Выбранные файлы отображаются списком под полем ввода,\n * каждый с кнопкой удаления (крестик).\n *\n * В onChange передаётся обновлённый массив результатов readFile.\n */\nexport type InputFilesProps =\n\t| (InputFilesCommonProps & {\n\t\t\tvalue: InputFilesDataUrlResult[];\n\t\t\tonChange: (value: InputFilesDataUrlResult[]) => void;\n\t\t\t/** Режим чтения: \"data-url\" по умолчанию */\n\t\t\treadMode?: \"data-url\";\n\t })\n\t| (InputFilesCommonProps & {\n\t\t\tvalue: InputFilesArrayBufferResult[];\n\t\t\tonChange: (value: InputFilesArrayBufferResult[]) => void;\n\t\t\t/** Режим чтения файла как ArrayBuffer */\n\t\t\treadMode: \"array-buffer\";\n\t });\n\n/**\n * InputFiles — компонент выбора нескольких файлов.\n *\n * Поле стилизовано как InputText. При клике открывается нативный\n * диалог с multiple. Каждый выбранный файл читается через readFile\n * и добавляется в массив value.\n *\n * Список выбранных файлов отображается под полем.\n * Каждый файл можно удалить кнопкой «крестик».\n *\n * InputFiles типизирует массив по `readMode`.\n * Если `readMode` не задан или равен `\"data-url\"`, в `value` и `onChange`\n * используется массив объектов с `dataUrl`. Если `readMode` равен\n * `\"array-buffer\"`, используется массив объектов с `buffer`.\n */\nexport function InputFiles(props: InputFilesProps) {\n\tconst {\n\t\tvalue,\n\t\tonReadError,\n\t\taccept,\n\t\tallowedMime,\n\t\tmaxBytes,\n\t\tmaxFiles,\n\t\tlabel,\n\t\tdescription,\n\t\tplaceholder = \"Выберите файлы\",\n\t\tdisabled,\n\t\tsize,\n\t\terror,\n\t\tonClearError,\n\t\tclassName\n\t} = props;\n\n\tassertValidAllowedMime(allowedMime);\n\tassertValidAccept(accept);\n\n\t/** Реф на скрытый нативный input[type=\"file\"] */\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst { controlId, labelId, descriptionId, errorId, describedBy } = useInputFieldIds({\n\t\thasLabel: label !== undefined && label !== null,\n\t\thasDescription: !!description,\n\t\thasError: !!error\n\t});\n\n\t/** Достигнут ли лимит файлов */\n\tconst isLimitReached = typeof maxFiles === \"number\" && value.length >= maxFiles;\n\n\t/**\n\t * Обработчик выбора файлов из нативного диалога.\n\t * Каждый файл читается через readFile.\n\t * Успешно прочитанные файлы добавляются к текущему массиву value.\n\t */\n\tconst handleChange = useCallback(\n\t\tasync (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\t\tconst files = e.target.files;\n\t\t\tif (!files || files.length === 0) return;\n\n\t\t\tconst selectedFiles = Array.from(files);\n\n\t\t\t// Сбрасываем значение input чтобы можно было выбрать те же файлы повторно\n\t\t\te.target.value = \"\";\n\n\t\t\t// Сбрасываем ошибку при выборе новых файлов\n\t\t\tif (error && onClearError) onClearError();\n\n\t\t\t// Определяем сколько файлов можно ещё добавить\n\t\t\tconst remaining = typeof maxFiles === \"number\" ? maxFiles - value.length : selectedFiles.length;\n\t\t\tconst filesToProcess = selectedFiles.slice(0, remaining);\n\n\t\t\tif (props.readMode === \"array-buffer\") {\n\t\t\t\t// Читаем файлы как ArrayBuffer и сохраняем точный тип массива для потребителя.\n\t\t\t\tconst results: InputFilesArrayBufferResult[] = [];\n\n\t\t\t\tfor (const file of filesToProcess) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await readFile(file, { allowedMime, maxBytes, mode: \"array-buffer\" });\n\t\t\t\t\t\tresults.push(result);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tif (err instanceof ReadFileError && onReadError) {\n\t\t\t\t\t\t\tonReadError(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (results.length > 0) {\n\t\t\t\t\tprops.onChange([...props.value, ...results]);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Читаем файлы как Data URL и сохраняем точный тип массива для потребителя.\n\t\t\tconst results: InputFilesDataUrlResult[] = [];\n\t\t\tfor (const file of filesToProcess) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await readFile(file, { allowedMime, maxBytes });\n\t\t\t\t\tresults.push(result);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (err instanceof ReadFileError && onReadError) {\n\t\t\t\t\t\tonReadError(err);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (results.length > 0) {\n\t\t\t\tprops.onChange([...props.value, ...results]);\n\t\t\t}\n\t\t},\n\t\t[props, maxFiles, value.length, allowedMime, maxBytes, onReadError, error, onClearError]\n\t);\n\n\t/** Клик по видимому контролу открывает нативный диалог */\n\tconst handleClick = useCallback(() => {\n\t\tinputRef.current?.click();\n\t}, []);\n\n\tconst filesList =\n\t\tprops.readMode === \"array-buffer\" ? (\n\t\t\t<InputFilesList files={props.value} onChange={props.onChange} />\n\t\t) : (\n\t\t\t<InputFilesList files={props.value} onChange={props.onChange} />\n\t\t);\n\n\treturn (\n\t\t<InputUI\n\t\t\tlabel={label}\n\t\t\tdescription={description}\n\t\t\tdisabled={disabled}\n\t\t\tclassName={className}\n\t\t\tsize={size}\n\t\t\terror={error}\n\t\t\tcontrolId={controlId}\n\t\t\tlabelId={labelId}\n\t\t\tdescriptionId={descriptionId}\n\t\t\terrorId={errorId}>\n\t\t\t<input\n\t\t\t\tref={inputRef}\n\t\t\t\tid={controlId}\n\t\t\t\ttype=\"file\"\n\t\t\t\taccept={(accept ?? allowedMime)?.join(\",\")}\n\t\t\t\tmultiple\n\t\t\t\tdisabled={disabled || isLimitReached}\n\t\t\t\tclassName={styles.hiddenInput}\n\t\t\t\tonChange={handleChange}\n\t\t\t\ttabIndex={-1}\n\t\t\t/>\n\n\t\t\t<InputControl>\n\t\t\t\t{() => (\n\t\t\t\t\t<div\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\ttabIndex={disabled || isLimitReached ? -1 : 0}\n\t\t\t\t\t\tonClick={handleClick}\n\t\t\t\t\t\tonKeyDown={(event) => handleKeyboardActivation(event, handleClick, { disabled: disabled || isLimitReached })}\n\t\t\t\t\t\tclassName={cn(uiStyles.uiInputControl, styles.control, !!error && \"invalid\")}\n\t\t\t\t\t\tdata-disabled={disabled || undefined}\n\t\t\t\t\t\taria-invalid={!!error || undefined}\n\t\t\t\t\t\taria-disabled={disabled || isLimitReached}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\taria-describedby={describedBy}>\n\t\t\t\t\t\t<span className={uiStyles.uiPlaceholder}>{isLimitReached ? `Максимум ${maxFiles} файлов` : placeholder}</span>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</InputControl>\n\n\t\t\t{/* Список выбранных файлов */}\n\t\t\t{filesList}\n\t\t</InputUI>\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e } from "./input-
|
|
1
|
+
import { n as e } from "./input-VC8jEoTa.js";
|
|
2
2
|
import { useState as t } from "react";
|
|
3
3
|
import { jsx as n } from "react/jsx-runtime";
|
|
4
4
|
//#region src/input-search/InputSearch.tsx
|
|
@@ -27,4 +27,4 @@ function r({ onChange: r, ...i }) {
|
|
|
27
27
|
//#endregion
|
|
28
28
|
export { r as t };
|
|
29
29
|
|
|
30
|
-
//# sourceMappingURL=input-search-
|
|
30
|
+
//# sourceMappingURL=input-search-Bs_9SxT0.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-search-
|
|
1
|
+
{"version":3,"file":"input-search-Bs_9SxT0.js","names":[],"sources":["../../src/input-search/InputSearch.tsx"],"sourcesContent":["import React, { useState } from \"react\";\n\nimport { BaseInputProps, Input } from \"../input\";\n\ninterface SearchInputProps extends BaseInputProps<string> {\n\tonChange: (value: string) => void;\n}\n\nexport function InputSearch({ onChange, ...props }: SearchInputProps) {\n\tconst [searchTerm, setSearchTerm] = useState(() => props.defaultValue ?? props.value ?? \"\");\n\tconst [previousSearch, setPreviousSearch] = useState(() => props.defaultValue ?? props.value ?? \"\");\n\n\tconst handleSearch = (term: string) => {\n\t\tconst changedTerm = term.trim();\n\n\t\t// Проверяем, изменилось ли значение с предыдущего поиска\n\t\tif (changedTerm === previousSearch) return;\n\n\t\tsetPreviousSearch(changedTerm);\n\t\tonChange(changedTerm);\n\t};\n\n\tconst handleClear = () => {\n\t\tsetSearchTerm(\"\");\n\t\tsetPreviousSearch(\"\");\n\t\tonChange(\"\");\n\t};\n\n\tconst handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n\t\tif (event.key === \"Enter\") {\n\t\t\tevent.preventDefault();\n\t\t\thandleSearch(searchTerm);\n\t\t}\n\t};\n\n\tconst handleBlur = () => {\n\t\thandleSearch(searchTerm);\n\t};\n\n\tconst handleChange = (value: string) => {\n\t\tsetSearchTerm(value);\n\t\t// Не вызываем onSearch здесь - только по Enter/blur\n\t};\n\n\treturn (\n\t\t<Input {...props} value={searchTerm} onChange={handleChange} onKeyDown={handleKeyDown} onBlur={handleBlur} onClear={handleClear} />\n\t);\n}\n"],"mappings":";;;;AAQA,SAAgB,EAAY,EAAE,aAAU,GAAG,KAA2B;CACrE,IAAM,CAAC,GAAY,KAAiB,QAAe,EAAM,gBAAgB,EAAM,SAAS,EAAE,GACpF,CAAC,GAAgB,KAAqB,QAAe,EAAM,gBAAgB,EAAM,SAAS,EAAE,GAE5F,KAAgB,MAAiB;EACtC,IAAM,IAAc,EAAK,KAAK;EAG1B,MAAgB,MAEpB,EAAkB,CAAW,GAC7B,EAAS,CAAW;CACrB,GAEM,UAAoB;EAGzB,AAFA,EAAc,EAAE,GAChB,EAAkB,EAAE,GACpB,EAAS,EAAE;CACZ,GAEM,KAAiB,MAAiD;EACvE,AAAI,EAAM,QAAQ,YACjB,EAAM,eAAe,GACrB,EAAa,CAAU;CAEzB,GAEM,UAAmB;EACxB,EAAa,CAAU;CACxB,GAEM,KAAgB,MAAkB;EACvC,EAAc,CAAK;CAEpB;CAEA,OACC,kBAAC,GAAD;EAAO,GAAI;EAAO,OAAO;EAAY,UAAU;EAAc,WAAW;EAAe,QAAQ;EAAY,SAAS;CAAc,CAAA;AAEpI"}
|