@de-kit/st-ui 0.1.8 → 0.1.9

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.
Files changed (34) hide show
  1. package/README.md +143 -143
  2. package/dist/components/button/button.cjs.map +1 -1
  3. package/dist/components/button/button.d.cts +1 -1
  4. package/dist/components/button/button.d.ts +1 -1
  5. package/dist/components/button/button.js.map +1 -1
  6. package/dist/components/button/button.scss +201 -147
  7. package/dist/components/checkbox/checkbox.cjs.map +1 -1
  8. package/dist/components/checkbox/checkbox.js.map +1 -1
  9. package/dist/components/checkbox/checkbox.scss +137 -137
  10. package/dist/components/field/field-context.cjs.map +1 -1
  11. package/dist/components/field/field-context.js.map +1 -1
  12. package/dist/components/field/field.cjs.map +1 -1
  13. package/dist/components/field/field.js.map +1 -1
  14. package/dist/components/field/field.scss +43 -43
  15. package/dist/components/input/input.cjs.map +1 -1
  16. package/dist/components/input/input.js.map +1 -1
  17. package/dist/components/input/input.scss +127 -127
  18. package/dist/components/radio/radio.cjs.map +1 -1
  19. package/dist/components/radio/radio.js.map +1 -1
  20. package/dist/components/radio/radio.scss +144 -144
  21. package/dist/components/spinner/spinner.cjs.map +1 -1
  22. package/dist/components/spinner/spinner.js.map +1 -1
  23. package/dist/components/spinner/spinner.scss +29 -29
  24. package/dist/components/textarea/textarea.cjs.map +1 -1
  25. package/dist/components/textarea/textarea.js.map +1 -1
  26. package/dist/components/textarea/textarea.scss +85 -85
  27. package/dist/icons/svg.cjs.map +1 -1
  28. package/dist/icons/svg.js.map +1 -1
  29. package/dist/lib/cn.cjs.map +1 -1
  30. package/dist/lib/cn.js.map +1 -1
  31. package/dist/styles/global.scss +201 -201
  32. package/dist/styles/mixins.scss +51 -51
  33. package/dist/styles/vars.scss +87 -87
  34. package/package.json +1 -1
@@ -1,137 +1,137 @@
1
- // data-uri не читает custom properties, поэтому галочка — маска: цвет берёт background-color.
2
- @function get-check-icon() {
3
- @return url("data:image/svg+xml,%3Csvg fill='%23fff' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' width='20px' height='20px'%3E%3Cpath d='M15.71,6.29c-0.39-0.39-1.02-0.39-1.41,0c-0.31,0.31-3.76,3.76-5.29,5.29c-0.81-0.81-2.01-2.01-2.29-2.29c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41c0.39,0.39,2.61,2.61,3,3c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,5.61-5.61,6-6C16.1,7.32,16.1,6.68,15.71,6.29z'/%3E%3C/svg%3E");
4
- }
5
-
6
- .checkbox {
7
- display: flex;
8
- align-items: flex-start;
9
- }
10
-
11
- .checkbox__label {
12
- font-size: var(--text-sm);
13
- line-height: var(--text-sm--line-height);
14
- display: inline-block;
15
- vertical-align: top;
16
- padding-left: var(--space-1);
17
- user-select: none;
18
- cursor: pointer;
19
- }
20
-
21
- .checkbox__input {
22
- --active: var(--color-primary);
23
- --active-inner: var(--color-primary-foreground);
24
- --border: var(--color-muted);
25
- --border-hover: var(--color-foreground);
26
- --background: var(--color-background);
27
- --disabled: var(--color-muted);
28
- --disabled-inner: var(--color-muted);
29
- box-sizing: border-box;
30
- appearance: none;
31
- width: 20px;
32
- height: 20px;
33
- outline: none;
34
- display: inline-block;
35
- vertical-align: top;
36
- position: relative;
37
- margin: 0;
38
- flex-shrink: 0;
39
- border: 1px solid var(--bc, var(--border));
40
- background: var(--b, var(--background));
41
- border-radius: var(--radius-sm);
42
- transition:
43
- background 0.3s,
44
- border-color 0.3s,
45
- box-shadow 0.2s;
46
- cursor: pointer;
47
-
48
- &::after {
49
- content: '';
50
- display: block;
51
- left: 0;
52
- top: 0;
53
- right: 0;
54
- bottom: 0;
55
- position: absolute;
56
- transition: opacity var(--d-o, 0.2s);
57
- opacity: var(--o, 0);
58
- background-color: var(--active-inner);
59
- -webkit-mask: get-check-icon() center / 20px 20px no-repeat;
60
- mask: get-check-icon() center / 20px 20px no-repeat;
61
- }
62
-
63
- // Красным красит состояние ошибки, а не сам по себе required: моментом ошибки
64
- // внутри Field распоряжается он. Рамка держится и под курсором — ховер только
65
- // добавляет к ней толщину.
66
- &[aria-invalid='true']:not(:disabled) {
67
- border-color: var(--color-destructive);
68
- }
69
-
70
- &:checked {
71
- --b: var(--active);
72
- --bc: var(--active);
73
- --o: 1;
74
- }
75
-
76
- &:indeterminate {
77
- --b: var(--active);
78
- --bc: var(--active);
79
- --o: 1;
80
-
81
- &::after {
82
- -webkit-mask: none;
83
- mask: none;
84
- top: 9px;
85
- right: 4px;
86
- bottom: 9px;
87
- left: 4px;
88
- }
89
- }
90
-
91
- &:hover:not(:checked):not(:disabled) {
92
- border-width: 2px;
93
- --bc: var(--active);
94
- }
95
-
96
- &:focus {
97
- border-color: var(--border-hover);
98
- border-width: 2px;
99
- }
100
-
101
- &:focus:not(:focus-visible) {
102
- outline: none;
103
- }
104
-
105
- &:focus-visible {
106
- outline: var(--focus-ring-width) solid var(--focus-ring-color);
107
- outline-offset: var(--focus-ring-offset);
108
- }
109
-
110
- &:disabled {
111
- --b: var(--disabled);
112
- border: 1px solid var(--disabled);
113
- cursor: not-allowed;
114
- opacity: 1;
115
-
116
- &:checked {
117
- --b: var(--disabled-inner);
118
- --active-inner: var(--color-muted-foreground);
119
- }
120
-
121
- & + .checkbox__label {
122
- color: var(--color-muted-foreground);
123
- cursor: not-allowed;
124
- }
125
- }
126
-
127
- &--no-events {
128
- pointer-events: none;
129
- }
130
- }
131
-
132
- @media (prefers-reduced-motion: reduce) {
133
- .checkbox__input,
134
- .checkbox__input::after {
135
- transition: none;
136
- }
137
- }
1
+ // data-uri не читает custom properties, поэтому галочка — маска: цвет берёт background-color.
2
+ @function get-check-icon() {
3
+ @return url("data:image/svg+xml,%3Csvg fill='%23fff' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' width='20px' height='20px'%3E%3Cpath d='M15.71,6.29c-0.39-0.39-1.02-0.39-1.41,0c-0.31,0.31-3.76,3.76-5.29,5.29c-0.81-0.81-2.01-2.01-2.29-2.29c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41c0.39,0.39,2.61,2.61,3,3c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,5.61-5.61,6-6C16.1,7.32,16.1,6.68,15.71,6.29z'/%3E%3C/svg%3E");
4
+ }
5
+
6
+ .checkbox {
7
+ display: flex;
8
+ align-items: flex-start;
9
+ }
10
+
11
+ .checkbox__label {
12
+ font-size: var(--text-sm);
13
+ line-height: var(--text-sm--line-height);
14
+ display: inline-block;
15
+ vertical-align: top;
16
+ padding-left: var(--space-1);
17
+ user-select: none;
18
+ cursor: pointer;
19
+ }
20
+
21
+ .checkbox__input {
22
+ --active: var(--color-primary);
23
+ --active-inner: var(--color-primary-foreground);
24
+ --border: var(--color-muted);
25
+ --border-hover: var(--color-foreground);
26
+ --background: var(--color-background);
27
+ --disabled: var(--color-muted);
28
+ --disabled-inner: var(--color-muted);
29
+ box-sizing: border-box;
30
+ appearance: none;
31
+ width: 20px;
32
+ height: 20px;
33
+ outline: none;
34
+ display: inline-block;
35
+ vertical-align: top;
36
+ position: relative;
37
+ margin: 0;
38
+ flex-shrink: 0;
39
+ border: 1px solid var(--bc, var(--border));
40
+ background: var(--b, var(--background));
41
+ border-radius: var(--radius-sm);
42
+ transition:
43
+ background 0.3s,
44
+ border-color 0.3s,
45
+ box-shadow 0.2s;
46
+ cursor: pointer;
47
+
48
+ &::after {
49
+ content: '';
50
+ display: block;
51
+ left: 0;
52
+ top: 0;
53
+ right: 0;
54
+ bottom: 0;
55
+ position: absolute;
56
+ transition: opacity var(--d-o, 0.2s);
57
+ opacity: var(--o, 0);
58
+ background-color: var(--active-inner);
59
+ -webkit-mask: get-check-icon() center / 20px 20px no-repeat;
60
+ mask: get-check-icon() center / 20px 20px no-repeat;
61
+ }
62
+
63
+ // Красным красит состояние ошибки, а не сам по себе required: моментом ошибки
64
+ // внутри Field распоряжается он. Рамка держится и под курсором — ховер только
65
+ // добавляет к ней толщину.
66
+ &[aria-invalid='true']:not(:disabled) {
67
+ border-color: var(--color-destructive);
68
+ }
69
+
70
+ &:checked {
71
+ --b: var(--active);
72
+ --bc: var(--active);
73
+ --o: 1;
74
+ }
75
+
76
+ &:indeterminate {
77
+ --b: var(--active);
78
+ --bc: var(--active);
79
+ --o: 1;
80
+
81
+ &::after {
82
+ -webkit-mask: none;
83
+ mask: none;
84
+ top: 9px;
85
+ right: 4px;
86
+ bottom: 9px;
87
+ left: 4px;
88
+ }
89
+ }
90
+
91
+ &:hover:not(:checked):not(:disabled) {
92
+ border-width: 2px;
93
+ --bc: var(--active);
94
+ }
95
+
96
+ &:focus {
97
+ border-color: var(--border-hover);
98
+ border-width: 2px;
99
+ }
100
+
101
+ &:focus:not(:focus-visible) {
102
+ outline: none;
103
+ }
104
+
105
+ &:focus-visible {
106
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
107
+ outline-offset: var(--focus-ring-offset);
108
+ }
109
+
110
+ &:disabled {
111
+ --b: var(--disabled);
112
+ border: 1px solid var(--disabled);
113
+ cursor: not-allowed;
114
+ opacity: 1;
115
+
116
+ &:checked {
117
+ --b: var(--disabled-inner);
118
+ --active-inner: var(--color-muted-foreground);
119
+ }
120
+
121
+ & + .checkbox__label {
122
+ color: var(--color-muted-foreground);
123
+ cursor: not-allowed;
124
+ }
125
+ }
126
+
127
+ &--no-events {
128
+ pointer-events: none;
129
+ }
130
+ }
131
+
132
+ @media (prefers-reduced-motion: reduce) {
133
+ .checkbox__input,
134
+ .checkbox__input::after {
135
+ transition: none;
136
+ }
137
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"field-context.cjs","names":["createContext","useContext"],"sources":["../../../src/components/field/field-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\r\n\r\nexport interface FieldControlContext {\r\n id: string;\r\n /** Для контролов без собственного лейбла: группа ссылается на него aria-labelledby. */\r\n labelId?: string;\r\n invalid: boolean;\r\n required: boolean;\r\n describedBy?: string;\r\n errorId?: string;\r\n /** Контрол сообщает показанное значение: текст поля или 'on' у отмеченного чекбокса. */\r\n reportValue?: (value: string) => void;\r\n}\r\n\r\nconst FieldControlReactContext = createContext<FieldControlContext | null>(\r\n null,\r\n);\r\n\r\nexport const FieldControlProvider = FieldControlReactContext.Provider;\r\n\r\nexport const useFieldControl = (): FieldControlContext | null =>\r\n useContext(FieldControlReactContext);\r\n"],"mappings":";;AAcA,MAAM,4BAAA,GAA2BA,MAAAA,cAAAA,CAC/B,IACF;AAEA,MAAa,uBAAuB,yBAAyB;AAE7D,MAAa,yBAAA,GACXC,MAAAA,WAAAA,CAAW,wBAAwB"}
1
+ {"version":3,"file":"field-context.cjs","names":["createContext","useContext"],"sources":["../../../src/components/field/field-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nexport interface FieldControlContext {\n id: string;\n /** Для контролов без собственного лейбла: группа ссылается на него aria-labelledby. */\n labelId?: string;\n invalid: boolean;\n required: boolean;\n describedBy?: string;\n errorId?: string;\n /** Контрол сообщает показанное значение: текст поля или 'on' у отмеченного чекбокса. */\n reportValue?: (value: string) => void;\n}\n\nconst FieldControlReactContext = createContext<FieldControlContext | null>(\n null,\n);\n\nexport const FieldControlProvider = FieldControlReactContext.Provider;\n\nexport const useFieldControl = (): FieldControlContext | null =>\n useContext(FieldControlReactContext);\n"],"mappings":";;AAcA,MAAM,4BAAA,GAA2BA,MAAAA,cAAAA,CAC/B,IACF;AAEA,MAAa,uBAAuB,yBAAyB;AAE7D,MAAa,yBAAA,GACXC,MAAAA,WAAAA,CAAW,wBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"field-context.js","names":[],"sources":["../../../src/components/field/field-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\r\n\r\nexport interface FieldControlContext {\r\n id: string;\r\n /** Для контролов без собственного лейбла: группа ссылается на него aria-labelledby. */\r\n labelId?: string;\r\n invalid: boolean;\r\n required: boolean;\r\n describedBy?: string;\r\n errorId?: string;\r\n /** Контрол сообщает показанное значение: текст поля или 'on' у отмеченного чекбокса. */\r\n reportValue?: (value: string) => void;\r\n}\r\n\r\nconst FieldControlReactContext = createContext<FieldControlContext | null>(\r\n null,\r\n);\r\n\r\nexport const FieldControlProvider = FieldControlReactContext.Provider;\r\n\r\nexport const useFieldControl = (): FieldControlContext | null =>\r\n useContext(FieldControlReactContext);\r\n"],"mappings":";;AAcA,MAAM,2BAA2B,cAC/B,IACF;AAEA,MAAa,uBAAuB,yBAAyB;AAE7D,MAAa,wBACX,WAAW,wBAAwB"}
1
+ {"version":3,"file":"field-context.js","names":[],"sources":["../../../src/components/field/field-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nexport interface FieldControlContext {\n id: string;\n /** Для контролов без собственного лейбла: группа ссылается на него aria-labelledby. */\n labelId?: string;\n invalid: boolean;\n required: boolean;\n describedBy?: string;\n errorId?: string;\n /** Контрол сообщает показанное значение: текст поля или 'on' у отмеченного чекбокса. */\n reportValue?: (value: string) => void;\n}\n\nconst FieldControlReactContext = createContext<FieldControlContext | null>(\n null,\n);\n\nexport const FieldControlProvider = FieldControlReactContext.Provider;\n\nexport const useFieldControl = (): FieldControlContext | null =>\n useContext(FieldControlReactContext);\n"],"mappings":";;AAcA,MAAM,2BAA2B,cAC/B,IACF;AAEA,MAAa,uBAAuB,yBAAyB;AAE7D,MAAa,wBACX,WAAW,wBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"field.cjs","names":["useId","useRef","useState","useCallback","useMemo","cn","when","FieldControlProvider"],"sources":["../../../src/components/field/field.tsx"],"sourcesContent":["import {\r\n useCallback,\r\n useEffect,\r\n useId,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from 'react';\r\n\r\nimport { cn, when } from '@/lib/cn';\r\nimport {\r\n FieldControlProvider,\r\n type FieldControlContext,\r\n} from './field-context';\r\nimport './field.scss';\r\n\r\nexport type FieldHintPlacement = 'bottom' | 'label';\r\n\r\nexport interface FieldProps {\r\n label: ReactNode;\r\n hint?: ReactNode;\r\n /** `bottom` — под полем, `label` — сразу под лейблом. */\r\n hintPlacement?: FieldHintPlacement;\r\n error?: ReactNode;\r\n /** Пустое значение после того, как в поле уже что-то было. */\r\n required?: boolean;\r\n /**\r\n * По умолчанию «Поле «{label}» обязательно», если лейбл — строка.\r\n * Слово «поле» среднего рода, поэтому формулировка не зависит от рода лейбла.\r\n */\r\n requiredMessage?: ReactNode;\r\n className?: string;\r\n /**\r\n * Один контрол: id он берёт у поля, поэтому свой `id` на ребёнке\r\n * перебивается, а два контрола внутри получат одинаковый.\r\n */\r\n children: ReactNode;\r\n}\r\n\r\nconst isFilled = (value: string) => value.trim() !== '';\r\n\r\nconst requiredText = (label: ReactNode, message: ReactNode | undefined) => {\r\n if (message) return message;\r\n if (typeof label === 'string') return `Поле «${label}» обязательно`;\r\n return 'Обязательное поле';\r\n};\r\n\r\nexport const Field = ({\r\n label,\r\n hint,\r\n hintPlacement = 'bottom',\r\n error,\r\n required = false,\r\n requiredMessage,\r\n className,\r\n children,\r\n}: FieldProps) => {\r\n const inputId = useId();\r\n const labelId = `${inputId}-label`;\r\n const rootRef = useRef<HTMLDivElement>(null);\r\n // ponytail: value/defaultValue больше не читаем с ребёнка. Контрол зовёт\r\n // reportValue из эффекта — до первого эффекта hadValue ещё false.\r\n const hadValueRef = useRef(false);\r\n const [showRequired, setShowRequired] = useState(false);\r\n const requiredError =\r\n required && showRequired ? requiredText(label, requiredMessage) : null;\r\n const visibleError = error ?? requiredError;\r\n const invalid = Boolean(visibleError);\r\n const hintId = hint ? `${inputId}-hint` : undefined;\r\n const errorId = invalid ? `${inputId}-error` : undefined;\r\n const describedBy = [errorId, hintId].filter(Boolean).join(' ');\r\n\r\n const reportValue = useCallback(\r\n (value: string) => {\r\n if (!required) return;\r\n\r\n if (isFilled(value)) {\r\n hadValueRef.current = true;\r\n setShowRequired(false);\r\n return;\r\n }\r\n\r\n if (hadValueRef.current) setShowRequired(true);\r\n },\r\n [required],\r\n );\r\n\r\n /**\r\n * Второй повод пожаловаться — неудачная проверка формы. Нужен для контролов,\r\n * которые нельзя опустошить: радиогруппу обратно не снимут, и до отправки\r\n * момента для ошибки нет. `invalid` не всплывает, но проходит фазу захвата,\r\n * поэтому одного слушателя на корне поля хватает на любой контрол внутри.\r\n * Реагируем только на пустое значение: формат, длина и прочие нарушения\r\n * обязательности не касаются, и чужую ошибку выдавать за неё нельзя.\r\n */\r\n useEffect(() => {\r\n const node = rootRef.current;\r\n if (!node || !required) return;\r\n\r\n const handleInvalid = (event: Event) => {\r\n const target = event.target;\r\n const missing =\r\n (target instanceof HTMLInputElement ||\r\n target instanceof HTMLTextAreaElement ||\r\n target instanceof HTMLSelectElement) &&\r\n target.validity.valueMissing;\r\n\r\n if (missing) setShowRequired(true);\r\n };\r\n\r\n node.addEventListener('invalid', handleInvalid, true);\r\n return () => node.removeEventListener('invalid', handleInvalid, true);\r\n }, [required]);\r\n\r\n const contextValue = useMemo<FieldControlContext>(\r\n () => ({\r\n id: inputId,\r\n labelId,\r\n invalid,\r\n required,\r\n describedBy: describedBy || undefined,\r\n errorId,\r\n reportValue,\r\n }),\r\n [describedBy, errorId, inputId, invalid, labelId, reportValue, required],\r\n );\r\n\r\n const hintByPlacement: Record<FieldHintPlacement, ReactNode> = {\r\n label: null,\r\n bottom: null,\r\n };\r\n\r\n if (hint) {\r\n hintByPlacement[hintPlacement] = (\r\n <p\r\n id={hintId}\r\n className={cn(\r\n 'field__hint',\r\n when(hintPlacement === 'label', 'field__hint--label'),\r\n )}\r\n >\r\n {hint}\r\n </p>\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n ref={rootRef}\r\n className={cn(\r\n 'field',\r\n className,\r\n when(Boolean(hint) && hintPlacement === 'label', 'field--hint-label'),\r\n )}\r\n >\r\n <label id={labelId} className=\"field__label\" htmlFor={inputId}>\r\n {label}\r\n </label>\r\n {hintByPlacement.label}\r\n <FieldControlProvider value={contextValue}>\r\n {children}\r\n </FieldControlProvider>\r\n {visibleError ? (\r\n <p id={errorId} className=\"field__error\" role=\"alert\">\r\n {visibleError}\r\n </p>\r\n ) : null}\r\n {hintByPlacement.bottom}\r\n </div>\r\n );\r\n};\r\n"],"mappings":";;;;;;AAwCA,MAAM,YAAY,UAAkB,MAAM,KAAK,MAAM;AAErD,MAAM,gBAAgB,OAAkB,YAAmC;CACzE,IAAI,SAAS,OAAO;CACpB,IAAI,OAAO,UAAU,UAAU,OAAO,SAAS,MAAM;CACrD,OAAO;AACT;AAEA,MAAa,SAAS,EACpB,OACA,MACA,gBAAgB,UAChB,OACA,WAAW,OACX,iBACA,WACA,eACgB;CAChB,MAAM,WAAA,GAAUA,MAAAA,MAAAA,CAAM;CACtB,MAAM,UAAU,GAAG,QAAQ;CAC3B,MAAM,WAAA,GAAUC,MAAAA,OAAAA,CAAuB,IAAI;CAG3C,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAAO,KAAK;CAChC,MAAM,CAAC,cAAc,oBAAA,GAAmBC,MAAAA,SAAAA,CAAS,KAAK;CACtD,MAAM,gBACJ,YAAY,eAAe,aAAa,OAAO,eAAe,IAAI;CACpE,MAAM,eAAe,SAAS;CAC9B,MAAM,UAAU,QAAQ,YAAY;CACpC,MAAM,SAAS,OAAO,GAAG,QAAQ,SAAS,KAAA;CAC1C,MAAM,UAAU,UAAU,GAAG,QAAQ,UAAU,KAAA;CAC/C,MAAM,cAAc,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE9D,MAAM,eAAA,GAAcC,MAAAA,YAAAA,EACjB,UAAkB;EACjB,IAAI,CAAC,UAAU;EAEf,IAAI,SAAS,KAAK,GAAG;GACnB,YAAY,UAAU;GACtB,gBAAgB,KAAK;GACrB;EACF;EAEA,IAAI,YAAY,SAAS,gBAAgB,IAAI;CAC/C,GACA,CAAC,QAAQ,CACX;;;;;;;;;CAUA,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,QAAQ,CAAC,UAAU;EAExB,MAAM,iBAAiB,UAAiB;GACtC,MAAM,SAAS,MAAM;GAOrB,KALG,kBAAkB,oBACjB,kBAAkB,uBAClB,kBAAkB,sBACpB,OAAO,SAAS,cAEL,gBAAgB,IAAI;EACnC;EAEA,KAAK,iBAAiB,WAAW,eAAe,IAAI;EACpD,aAAa,KAAK,oBAAoB,WAAW,eAAe,IAAI;CACtE,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,gBAAA,GAAeC,MAAAA,QAAAA,QACZ;EACL,IAAI;EACJ;EACA;EACA;EACA,aAAa,eAAe,KAAA;EAC5B;EACA;CACF,IACA;EAAC;EAAa;EAAS;EAAS;EAAS;EAAS;EAAa;CAAQ,CACzE;CAEA,MAAM,kBAAyD;EAC7D,OAAO;EACP,QAAQ;CACV;CAEA,IAAI,MACF,gBAAgB,iBACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;EACE,IAAI;EACJ,WAAWC,WAAAA,GACT,eACAC,WAAAA,KAAK,kBAAkB,SAAS,oBAAoB,CACtD;EAEC,UAAA;CACA,CAAA;CAIP,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EACE,KAAK;EACL,WAAWD,WAAAA,GACT,SACA,WACAC,WAAAA,KAAK,QAAQ,IAAI,KAAK,kBAAkB,SAAS,mBAAmB,CACtE;EANF,UAAA;GAQE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;IAAO,IAAI;IAAS,WAAU;IAAe,SAAS;IACnD,UAAA;GACI,CAAA;GACN,gBAAgB;GACjB,iBAAA,GAAA,kBAAA,IAAA,CAACC,sBAAAA,sBAAD;IAAsB,OAAO;IAC1B;GACmB,CAAA;GACrB,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,IAAI;IAAS,WAAU;IAAe,MAAK;IAC3C,UAAA;GACA,CAAA,IACD;GACH,gBAAgB;EACd;;AAET"}
1
+ {"version":3,"file":"field.cjs","names":["useId","useRef","useState","useCallback","useMemo","cn","when","FieldControlProvider"],"sources":["../../../src/components/field/field.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport { cn, when } from '@/lib/cn';\nimport {\n FieldControlProvider,\n type FieldControlContext,\n} from './field-context';\nimport './field.scss';\n\nexport type FieldHintPlacement = 'bottom' | 'label';\n\nexport interface FieldProps {\n label: ReactNode;\n hint?: ReactNode;\n /** `bottom` — под полем, `label` — сразу под лейблом. */\n hintPlacement?: FieldHintPlacement;\n error?: ReactNode;\n /** Пустое значение после того, как в поле уже что-то было. */\n required?: boolean;\n /**\n * По умолчанию «Поле «{label}» обязательно», если лейбл — строка.\n * Слово «поле» среднего рода, поэтому формулировка не зависит от рода лейбла.\n */\n requiredMessage?: ReactNode;\n className?: string;\n /**\n * Один контрол: id он берёт у поля, поэтому свой `id` на ребёнке\n * перебивается, а два контрола внутри получат одинаковый.\n */\n children: ReactNode;\n}\n\nconst isFilled = (value: string) => value.trim() !== '';\n\nconst requiredText = (label: ReactNode, message: ReactNode | undefined) => {\n if (message) return message;\n if (typeof label === 'string') return `Поле «${label}» обязательно`;\n return 'Обязательное поле';\n};\n\nexport const Field = ({\n label,\n hint,\n hintPlacement = 'bottom',\n error,\n required = false,\n requiredMessage,\n className,\n children,\n}: FieldProps) => {\n const inputId = useId();\n const labelId = `${inputId}-label`;\n const rootRef = useRef<HTMLDivElement>(null);\n // ponytail: value/defaultValue больше не читаем с ребёнка. Контрол зовёт\n // reportValue из эффекта — до первого эффекта hadValue ещё false.\n const hadValueRef = useRef(false);\n const [showRequired, setShowRequired] = useState(false);\n const requiredError =\n required && showRequired ? requiredText(label, requiredMessage) : null;\n const visibleError = error ?? requiredError;\n const invalid = Boolean(visibleError);\n const hintId = hint ? `${inputId}-hint` : undefined;\n const errorId = invalid ? `${inputId}-error` : undefined;\n const describedBy = [errorId, hintId].filter(Boolean).join(' ');\n\n const reportValue = useCallback(\n (value: string) => {\n if (!required) return;\n\n if (isFilled(value)) {\n hadValueRef.current = true;\n setShowRequired(false);\n return;\n }\n\n if (hadValueRef.current) setShowRequired(true);\n },\n [required],\n );\n\n /**\n * Второй повод пожаловаться — неудачная проверка формы. Нужен для контролов,\n * которые нельзя опустошить: радиогруппу обратно не снимут, и до отправки\n * момента для ошибки нет. `invalid` не всплывает, но проходит фазу захвата,\n * поэтому одного слушателя на корне поля хватает на любой контрол внутри.\n * Реагируем только на пустое значение: формат, длина и прочие нарушения\n * обязательности не касаются, и чужую ошибку выдавать за неё нельзя.\n */\n useEffect(() => {\n const node = rootRef.current;\n if (!node || !required) return;\n\n const handleInvalid = (event: Event) => {\n const target = event.target;\n const missing =\n (target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n target instanceof HTMLSelectElement) &&\n target.validity.valueMissing;\n\n if (missing) setShowRequired(true);\n };\n\n node.addEventListener('invalid', handleInvalid, true);\n return () => node.removeEventListener('invalid', handleInvalid, true);\n }, [required]);\n\n const contextValue = useMemo<FieldControlContext>(\n () => ({\n id: inputId,\n labelId,\n invalid,\n required,\n describedBy: describedBy || undefined,\n errorId,\n reportValue,\n }),\n [describedBy, errorId, inputId, invalid, labelId, reportValue, required],\n );\n\n const hintByPlacement: Record<FieldHintPlacement, ReactNode> = {\n label: null,\n bottom: null,\n };\n\n if (hint) {\n hintByPlacement[hintPlacement] = (\n <p\n id={hintId}\n className={cn(\n 'field__hint',\n when(hintPlacement === 'label', 'field__hint--label'),\n )}\n >\n {hint}\n </p>\n );\n }\n\n return (\n <div\n ref={rootRef}\n className={cn(\n 'field',\n className,\n when(Boolean(hint) && hintPlacement === 'label', 'field--hint-label'),\n )}\n >\n <label id={labelId} className=\"field__label\" htmlFor={inputId}>\n {label}\n </label>\n {hintByPlacement.label}\n <FieldControlProvider value={contextValue}>\n {children}\n </FieldControlProvider>\n {visibleError ? (\n <p id={errorId} className=\"field__error\" role=\"alert\">\n {visibleError}\n </p>\n ) : null}\n {hintByPlacement.bottom}\n </div>\n );\n};\n"],"mappings":";;;;;;AAwCA,MAAM,YAAY,UAAkB,MAAM,KAAK,MAAM;AAErD,MAAM,gBAAgB,OAAkB,YAAmC;CACzE,IAAI,SAAS,OAAO;CACpB,IAAI,OAAO,UAAU,UAAU,OAAO,SAAS,MAAM;CACrD,OAAO;AACT;AAEA,MAAa,SAAS,EACpB,OACA,MACA,gBAAgB,UAChB,OACA,WAAW,OACX,iBACA,WACA,eACgB;CAChB,MAAM,WAAA,GAAUA,MAAAA,MAAAA,CAAM;CACtB,MAAM,UAAU,GAAG,QAAQ;CAC3B,MAAM,WAAA,GAAUC,MAAAA,OAAAA,CAAuB,IAAI;CAG3C,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAAO,KAAK;CAChC,MAAM,CAAC,cAAc,oBAAA,GAAmBC,MAAAA,SAAAA,CAAS,KAAK;CACtD,MAAM,gBACJ,YAAY,eAAe,aAAa,OAAO,eAAe,IAAI;CACpE,MAAM,eAAe,SAAS;CAC9B,MAAM,UAAU,QAAQ,YAAY;CACpC,MAAM,SAAS,OAAO,GAAG,QAAQ,SAAS,KAAA;CAC1C,MAAM,UAAU,UAAU,GAAG,QAAQ,UAAU,KAAA;CAC/C,MAAM,cAAc,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE9D,MAAM,eAAA,GAAcC,MAAAA,YAAAA,EACjB,UAAkB;EACjB,IAAI,CAAC,UAAU;EAEf,IAAI,SAAS,KAAK,GAAG;GACnB,YAAY,UAAU;GACtB,gBAAgB,KAAK;GACrB;EACF;EAEA,IAAI,YAAY,SAAS,gBAAgB,IAAI;CAC/C,GACA,CAAC,QAAQ,CACX;;;;;;;;;CAUA,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,QAAQ,CAAC,UAAU;EAExB,MAAM,iBAAiB,UAAiB;GACtC,MAAM,SAAS,MAAM;GAOrB,KALG,kBAAkB,oBACjB,kBAAkB,uBAClB,kBAAkB,sBACpB,OAAO,SAAS,cAEL,gBAAgB,IAAI;EACnC;EAEA,KAAK,iBAAiB,WAAW,eAAe,IAAI;EACpD,aAAa,KAAK,oBAAoB,WAAW,eAAe,IAAI;CACtE,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,gBAAA,GAAeC,MAAAA,QAAAA,QACZ;EACL,IAAI;EACJ;EACA;EACA;EACA,aAAa,eAAe,KAAA;EAC5B;EACA;CACF,IACA;EAAC;EAAa;EAAS;EAAS;EAAS;EAAS;EAAa;CAAQ,CACzE;CAEA,MAAM,kBAAyD;EAC7D,OAAO;EACP,QAAQ;CACV;CAEA,IAAI,MACF,gBAAgB,iBACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;EACE,IAAI;EACJ,WAAWC,WAAAA,GACT,eACAC,WAAAA,KAAK,kBAAkB,SAAS,oBAAoB,CACtD;EAEC,UAAA;CACA,CAAA;CAIP,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EACE,KAAK;EACL,WAAWD,WAAAA,GACT,SACA,WACAC,WAAAA,KAAK,QAAQ,IAAI,KAAK,kBAAkB,SAAS,mBAAmB,CACtE;EANF,UAAA;GAQE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;IAAO,IAAI;IAAS,WAAU;IAAe,SAAS;IACnD,UAAA;GACI,CAAA;GACN,gBAAgB;GACjB,iBAAA,GAAA,kBAAA,IAAA,CAACC,sBAAAA,sBAAD;IAAsB,OAAO;IAC1B;GACmB,CAAA;GACrB,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,IAAI;IAAS,WAAU;IAAe,MAAK;IAC3C,UAAA;GACA,CAAA,IACD;GACH,gBAAgB;EACd;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","names":[],"sources":["../../../src/components/field/field.tsx"],"sourcesContent":["import {\r\n useCallback,\r\n useEffect,\r\n useId,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from 'react';\r\n\r\nimport { cn, when } from '@/lib/cn';\r\nimport {\r\n FieldControlProvider,\r\n type FieldControlContext,\r\n} from './field-context';\r\nimport './field.scss';\r\n\r\nexport type FieldHintPlacement = 'bottom' | 'label';\r\n\r\nexport interface FieldProps {\r\n label: ReactNode;\r\n hint?: ReactNode;\r\n /** `bottom` — под полем, `label` — сразу под лейблом. */\r\n hintPlacement?: FieldHintPlacement;\r\n error?: ReactNode;\r\n /** Пустое значение после того, как в поле уже что-то было. */\r\n required?: boolean;\r\n /**\r\n * По умолчанию «Поле «{label}» обязательно», если лейбл — строка.\r\n * Слово «поле» среднего рода, поэтому формулировка не зависит от рода лейбла.\r\n */\r\n requiredMessage?: ReactNode;\r\n className?: string;\r\n /**\r\n * Один контрол: id он берёт у поля, поэтому свой `id` на ребёнке\r\n * перебивается, а два контрола внутри получат одинаковый.\r\n */\r\n children: ReactNode;\r\n}\r\n\r\nconst isFilled = (value: string) => value.trim() !== '';\r\n\r\nconst requiredText = (label: ReactNode, message: ReactNode | undefined) => {\r\n if (message) return message;\r\n if (typeof label === 'string') return `Поле «${label}» обязательно`;\r\n return 'Обязательное поле';\r\n};\r\n\r\nexport const Field = ({\r\n label,\r\n hint,\r\n hintPlacement = 'bottom',\r\n error,\r\n required = false,\r\n requiredMessage,\r\n className,\r\n children,\r\n}: FieldProps) => {\r\n const inputId = useId();\r\n const labelId = `${inputId}-label`;\r\n const rootRef = useRef<HTMLDivElement>(null);\r\n // ponytail: value/defaultValue больше не читаем с ребёнка. Контрол зовёт\r\n // reportValue из эффекта — до первого эффекта hadValue ещё false.\r\n const hadValueRef = useRef(false);\r\n const [showRequired, setShowRequired] = useState(false);\r\n const requiredError =\r\n required && showRequired ? requiredText(label, requiredMessage) : null;\r\n const visibleError = error ?? requiredError;\r\n const invalid = Boolean(visibleError);\r\n const hintId = hint ? `${inputId}-hint` : undefined;\r\n const errorId = invalid ? `${inputId}-error` : undefined;\r\n const describedBy = [errorId, hintId].filter(Boolean).join(' ');\r\n\r\n const reportValue = useCallback(\r\n (value: string) => {\r\n if (!required) return;\r\n\r\n if (isFilled(value)) {\r\n hadValueRef.current = true;\r\n setShowRequired(false);\r\n return;\r\n }\r\n\r\n if (hadValueRef.current) setShowRequired(true);\r\n },\r\n [required],\r\n );\r\n\r\n /**\r\n * Второй повод пожаловаться — неудачная проверка формы. Нужен для контролов,\r\n * которые нельзя опустошить: радиогруппу обратно не снимут, и до отправки\r\n * момента для ошибки нет. `invalid` не всплывает, но проходит фазу захвата,\r\n * поэтому одного слушателя на корне поля хватает на любой контрол внутри.\r\n * Реагируем только на пустое значение: формат, длина и прочие нарушения\r\n * обязательности не касаются, и чужую ошибку выдавать за неё нельзя.\r\n */\r\n useEffect(() => {\r\n const node = rootRef.current;\r\n if (!node || !required) return;\r\n\r\n const handleInvalid = (event: Event) => {\r\n const target = event.target;\r\n const missing =\r\n (target instanceof HTMLInputElement ||\r\n target instanceof HTMLTextAreaElement ||\r\n target instanceof HTMLSelectElement) &&\r\n target.validity.valueMissing;\r\n\r\n if (missing) setShowRequired(true);\r\n };\r\n\r\n node.addEventListener('invalid', handleInvalid, true);\r\n return () => node.removeEventListener('invalid', handleInvalid, true);\r\n }, [required]);\r\n\r\n const contextValue = useMemo<FieldControlContext>(\r\n () => ({\r\n id: inputId,\r\n labelId,\r\n invalid,\r\n required,\r\n describedBy: describedBy || undefined,\r\n errorId,\r\n reportValue,\r\n }),\r\n [describedBy, errorId, inputId, invalid, labelId, reportValue, required],\r\n );\r\n\r\n const hintByPlacement: Record<FieldHintPlacement, ReactNode> = {\r\n label: null,\r\n bottom: null,\r\n };\r\n\r\n if (hint) {\r\n hintByPlacement[hintPlacement] = (\r\n <p\r\n id={hintId}\r\n className={cn(\r\n 'field__hint',\r\n when(hintPlacement === 'label', 'field__hint--label'),\r\n )}\r\n >\r\n {hint}\r\n </p>\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n ref={rootRef}\r\n className={cn(\r\n 'field',\r\n className,\r\n when(Boolean(hint) && hintPlacement === 'label', 'field--hint-label'),\r\n )}\r\n >\r\n <label id={labelId} className=\"field__label\" htmlFor={inputId}>\r\n {label}\r\n </label>\r\n {hintByPlacement.label}\r\n <FieldControlProvider value={contextValue}>\r\n {children}\r\n </FieldControlProvider>\r\n {visibleError ? (\r\n <p id={errorId} className=\"field__error\" role=\"alert\">\r\n {visibleError}\r\n </p>\r\n ) : null}\r\n {hintByPlacement.bottom}\r\n </div>\r\n );\r\n};\r\n"],"mappings":";;;;;;AAwCA,MAAM,YAAY,UAAkB,MAAM,KAAK,MAAM;AAErD,MAAM,gBAAgB,OAAkB,YAAmC;CACzE,IAAI,SAAS,OAAO;CACpB,IAAI,OAAO,UAAU,UAAU,OAAO,SAAS,MAAM;CACrD,OAAO;AACT;AAEA,MAAa,SAAS,EACpB,OACA,MACA,gBAAgB,UAChB,OACA,WAAW,OACX,iBACA,WACA,eACgB;CAChB,MAAM,UAAU,MAAM;CACtB,MAAM,UAAU,GAAG,QAAQ;CAC3B,MAAM,UAAU,OAAuB,IAAI;CAG3C,MAAM,cAAc,OAAO,KAAK;CAChC,MAAM,CAAC,cAAc,mBAAmB,SAAS,KAAK;CACtD,MAAM,gBACJ,YAAY,eAAe,aAAa,OAAO,eAAe,IAAI;CACpE,MAAM,eAAe,SAAS;CAC9B,MAAM,UAAU,QAAQ,YAAY;CACpC,MAAM,SAAS,OAAO,GAAG,QAAQ,SAAS,KAAA;CAC1C,MAAM,UAAU,UAAU,GAAG,QAAQ,UAAU,KAAA;CAC/C,MAAM,cAAc,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE9D,MAAM,cAAc,aACjB,UAAkB;EACjB,IAAI,CAAC,UAAU;EAEf,IAAI,SAAS,KAAK,GAAG;GACnB,YAAY,UAAU;GACtB,gBAAgB,KAAK;GACrB;EACF;EAEA,IAAI,YAAY,SAAS,gBAAgB,IAAI;CAC/C,GACA,CAAC,QAAQ,CACX;;;;;;;;;CAUA,gBAAgB;EACd,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,QAAQ,CAAC,UAAU;EAExB,MAAM,iBAAiB,UAAiB;GACtC,MAAM,SAAS,MAAM;GAOrB,KALG,kBAAkB,oBACjB,kBAAkB,uBAClB,kBAAkB,sBACpB,OAAO,SAAS,cAEL,gBAAgB,IAAI;EACnC;EAEA,KAAK,iBAAiB,WAAW,eAAe,IAAI;EACpD,aAAa,KAAK,oBAAoB,WAAW,eAAe,IAAI;CACtE,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,eAAe,eACZ;EACL,IAAI;EACJ;EACA;EACA;EACA,aAAa,eAAe,KAAA;EAC5B;EACA;CACF,IACA;EAAC;EAAa;EAAS;EAAS;EAAS;EAAS;EAAa;CAAQ,CACzE;CAEA,MAAM,kBAAyD;EAC7D,OAAO;EACP,QAAQ;CACV;CAEA,IAAI,MACF,gBAAgB,iBACd,oBAAC,KAAD;EACE,IAAI;EACJ,WAAW,GACT,eACA,KAAK,kBAAkB,SAAS,oBAAoB,CACtD;EAEC,UAAA;CACA,CAAA;CAIP,OACE,qBAAC,OAAD;EACE,KAAK;EACL,WAAW,GACT,SACA,WACA,KAAK,QAAQ,IAAI,KAAK,kBAAkB,SAAS,mBAAmB,CACtE;EANF,UAAA;GAQE,oBAAC,SAAD;IAAO,IAAI;IAAS,WAAU;IAAe,SAAS;IACnD,UAAA;GACI,CAAA;GACN,gBAAgB;GACjB,oBAAC,sBAAD;IAAsB,OAAO;IAC1B;GACmB,CAAA;GACrB,eACC,oBAAC,KAAD;IAAG,IAAI;IAAS,WAAU;IAAe,MAAK;IAC3C,UAAA;GACA,CAAA,IACD;GACH,gBAAgB;EACd;;AAET"}
1
+ {"version":3,"file":"field.js","names":[],"sources":["../../../src/components/field/field.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport { cn, when } from '@/lib/cn';\nimport {\n FieldControlProvider,\n type FieldControlContext,\n} from './field-context';\nimport './field.scss';\n\nexport type FieldHintPlacement = 'bottom' | 'label';\n\nexport interface FieldProps {\n label: ReactNode;\n hint?: ReactNode;\n /** `bottom` — под полем, `label` — сразу под лейблом. */\n hintPlacement?: FieldHintPlacement;\n error?: ReactNode;\n /** Пустое значение после того, как в поле уже что-то было. */\n required?: boolean;\n /**\n * По умолчанию «Поле «{label}» обязательно», если лейбл — строка.\n * Слово «поле» среднего рода, поэтому формулировка не зависит от рода лейбла.\n */\n requiredMessage?: ReactNode;\n className?: string;\n /**\n * Один контрол: id он берёт у поля, поэтому свой `id` на ребёнке\n * перебивается, а два контрола внутри получат одинаковый.\n */\n children: ReactNode;\n}\n\nconst isFilled = (value: string) => value.trim() !== '';\n\nconst requiredText = (label: ReactNode, message: ReactNode | undefined) => {\n if (message) return message;\n if (typeof label === 'string') return `Поле «${label}» обязательно`;\n return 'Обязательное поле';\n};\n\nexport const Field = ({\n label,\n hint,\n hintPlacement = 'bottom',\n error,\n required = false,\n requiredMessage,\n className,\n children,\n}: FieldProps) => {\n const inputId = useId();\n const labelId = `${inputId}-label`;\n const rootRef = useRef<HTMLDivElement>(null);\n // ponytail: value/defaultValue больше не читаем с ребёнка. Контрол зовёт\n // reportValue из эффекта — до первого эффекта hadValue ещё false.\n const hadValueRef = useRef(false);\n const [showRequired, setShowRequired] = useState(false);\n const requiredError =\n required && showRequired ? requiredText(label, requiredMessage) : null;\n const visibleError = error ?? requiredError;\n const invalid = Boolean(visibleError);\n const hintId = hint ? `${inputId}-hint` : undefined;\n const errorId = invalid ? `${inputId}-error` : undefined;\n const describedBy = [errorId, hintId].filter(Boolean).join(' ');\n\n const reportValue = useCallback(\n (value: string) => {\n if (!required) return;\n\n if (isFilled(value)) {\n hadValueRef.current = true;\n setShowRequired(false);\n return;\n }\n\n if (hadValueRef.current) setShowRequired(true);\n },\n [required],\n );\n\n /**\n * Второй повод пожаловаться — неудачная проверка формы. Нужен для контролов,\n * которые нельзя опустошить: радиогруппу обратно не снимут, и до отправки\n * момента для ошибки нет. `invalid` не всплывает, но проходит фазу захвата,\n * поэтому одного слушателя на корне поля хватает на любой контрол внутри.\n * Реагируем только на пустое значение: формат, длина и прочие нарушения\n * обязательности не касаются, и чужую ошибку выдавать за неё нельзя.\n */\n useEffect(() => {\n const node = rootRef.current;\n if (!node || !required) return;\n\n const handleInvalid = (event: Event) => {\n const target = event.target;\n const missing =\n (target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n target instanceof HTMLSelectElement) &&\n target.validity.valueMissing;\n\n if (missing) setShowRequired(true);\n };\n\n node.addEventListener('invalid', handleInvalid, true);\n return () => node.removeEventListener('invalid', handleInvalid, true);\n }, [required]);\n\n const contextValue = useMemo<FieldControlContext>(\n () => ({\n id: inputId,\n labelId,\n invalid,\n required,\n describedBy: describedBy || undefined,\n errorId,\n reportValue,\n }),\n [describedBy, errorId, inputId, invalid, labelId, reportValue, required],\n );\n\n const hintByPlacement: Record<FieldHintPlacement, ReactNode> = {\n label: null,\n bottom: null,\n };\n\n if (hint) {\n hintByPlacement[hintPlacement] = (\n <p\n id={hintId}\n className={cn(\n 'field__hint',\n when(hintPlacement === 'label', 'field__hint--label'),\n )}\n >\n {hint}\n </p>\n );\n }\n\n return (\n <div\n ref={rootRef}\n className={cn(\n 'field',\n className,\n when(Boolean(hint) && hintPlacement === 'label', 'field--hint-label'),\n )}\n >\n <label id={labelId} className=\"field__label\" htmlFor={inputId}>\n {label}\n </label>\n {hintByPlacement.label}\n <FieldControlProvider value={contextValue}>\n {children}\n </FieldControlProvider>\n {visibleError ? (\n <p id={errorId} className=\"field__error\" role=\"alert\">\n {visibleError}\n </p>\n ) : null}\n {hintByPlacement.bottom}\n </div>\n );\n};\n"],"mappings":";;;;;;AAwCA,MAAM,YAAY,UAAkB,MAAM,KAAK,MAAM;AAErD,MAAM,gBAAgB,OAAkB,YAAmC;CACzE,IAAI,SAAS,OAAO;CACpB,IAAI,OAAO,UAAU,UAAU,OAAO,SAAS,MAAM;CACrD,OAAO;AACT;AAEA,MAAa,SAAS,EACpB,OACA,MACA,gBAAgB,UAChB,OACA,WAAW,OACX,iBACA,WACA,eACgB;CAChB,MAAM,UAAU,MAAM;CACtB,MAAM,UAAU,GAAG,QAAQ;CAC3B,MAAM,UAAU,OAAuB,IAAI;CAG3C,MAAM,cAAc,OAAO,KAAK;CAChC,MAAM,CAAC,cAAc,mBAAmB,SAAS,KAAK;CACtD,MAAM,gBACJ,YAAY,eAAe,aAAa,OAAO,eAAe,IAAI;CACpE,MAAM,eAAe,SAAS;CAC9B,MAAM,UAAU,QAAQ,YAAY;CACpC,MAAM,SAAS,OAAO,GAAG,QAAQ,SAAS,KAAA;CAC1C,MAAM,UAAU,UAAU,GAAG,QAAQ,UAAU,KAAA;CAC/C,MAAM,cAAc,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE9D,MAAM,cAAc,aACjB,UAAkB;EACjB,IAAI,CAAC,UAAU;EAEf,IAAI,SAAS,KAAK,GAAG;GACnB,YAAY,UAAU;GACtB,gBAAgB,KAAK;GACrB;EACF;EAEA,IAAI,YAAY,SAAS,gBAAgB,IAAI;CAC/C,GACA,CAAC,QAAQ,CACX;;;;;;;;;CAUA,gBAAgB;EACd,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,QAAQ,CAAC,UAAU;EAExB,MAAM,iBAAiB,UAAiB;GACtC,MAAM,SAAS,MAAM;GAOrB,KALG,kBAAkB,oBACjB,kBAAkB,uBAClB,kBAAkB,sBACpB,OAAO,SAAS,cAEL,gBAAgB,IAAI;EACnC;EAEA,KAAK,iBAAiB,WAAW,eAAe,IAAI;EACpD,aAAa,KAAK,oBAAoB,WAAW,eAAe,IAAI;CACtE,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,eAAe,eACZ;EACL,IAAI;EACJ;EACA;EACA;EACA,aAAa,eAAe,KAAA;EAC5B;EACA;CACF,IACA;EAAC;EAAa;EAAS;EAAS;EAAS;EAAS;EAAa;CAAQ,CACzE;CAEA,MAAM,kBAAyD;EAC7D,OAAO;EACP,QAAQ;CACV;CAEA,IAAI,MACF,gBAAgB,iBACd,oBAAC,KAAD;EACE,IAAI;EACJ,WAAW,GACT,eACA,KAAK,kBAAkB,SAAS,oBAAoB,CACtD;EAEC,UAAA;CACA,CAAA;CAIP,OACE,qBAAC,OAAD;EACE,KAAK;EACL,WAAW,GACT,SACA,WACA,KAAK,QAAQ,IAAI,KAAK,kBAAkB,SAAS,mBAAmB,CACtE;EANF,UAAA;GAQE,oBAAC,SAAD;IAAO,IAAI;IAAS,WAAU;IAAe,SAAS;IACnD,UAAA;GACI,CAAA;GACN,gBAAgB;GACjB,oBAAC,sBAAD;IAAsB,OAAO;IAC1B;GACmB,CAAA;GACrB,eACC,oBAAC,KAAD;IAAG,IAAI;IAAS,WAAU;IAAe,MAAK;IAC3C,UAAA;GACA,CAAA,IACD;GACH,gBAAgB;EACd;;AAET"}
@@ -1,43 +1,43 @@
1
- .field {
2
- display: flex;
3
- flex-direction: column;
4
- width: 100%;
5
- }
6
-
7
- .field__label {
8
- color: var(--color-foreground);
9
- font-size: var(--text-lg);
10
- font-weight: 900;
11
- line-height: var(--text-lg--line-height);
12
- display: block;
13
- margin-bottom: var(--space-3);
14
- overflow-wrap: anywhere;
15
- -webkit-hyphens: auto;
16
- hyphens: auto;
17
- }
18
-
19
- .field__hint,
20
- .field__error {
21
- margin: var(--space-2) 0 0 0;
22
- font-size: var(--text-xs);
23
- line-height: var(--text-xs--line-height);
24
- }
25
-
26
- .field__hint {
27
- color: var(--color-muted-foreground);
28
- }
29
-
30
- .field--hint-label .field__label {
31
- margin-bottom: var(--space-2);
32
- }
33
-
34
- .field__hint--label {
35
- margin: 0 0 var(--space-3);
36
- font-size: var(--text-sm);
37
- line-height: var(--text-sm--line-height);
38
- color: var(--color-foreground);
39
- }
40
-
41
- .field__error {
42
- color: var(--color-destructive);
43
- }
1
+ .field {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ }
6
+
7
+ .field__label {
8
+ color: var(--color-foreground);
9
+ font-size: var(--text-lg);
10
+ font-weight: 900;
11
+ line-height: var(--text-lg--line-height);
12
+ display: block;
13
+ margin-bottom: var(--space-3);
14
+ overflow-wrap: anywhere;
15
+ -webkit-hyphens: auto;
16
+ hyphens: auto;
17
+ }
18
+
19
+ .field__hint,
20
+ .field__error {
21
+ margin: var(--space-2) 0 0 0;
22
+ font-size: var(--text-xs);
23
+ line-height: var(--text-xs--line-height);
24
+ }
25
+
26
+ .field__hint {
27
+ color: var(--color-muted-foreground);
28
+ }
29
+
30
+ .field--hint-label .field__label {
31
+ margin-bottom: var(--space-2);
32
+ }
33
+
34
+ .field__hint--label {
35
+ margin: 0 0 var(--space-3);
36
+ font-size: var(--text-sm);
37
+ line-height: var(--text-sm--line-height);
38
+ color: var(--color-foreground);
39
+ }
40
+
41
+ .field__error {
42
+ color: var(--color-destructive);
43
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"input.cjs","names":["forwardRef","useFieldControl","useRef","useState","useCallback","cn","when","IconCross"],"sources":["../../../src/components/input/input.tsx"],"sourcesContent":["import {\r\n forwardRef,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n type ChangeEvent,\r\n type InputHTMLAttributes,\r\n type MouseEvent,\r\n type Ref,\r\n} from 'react';\r\n\r\nimport { useFieldControl } from '@/components/field/field-context';\r\nimport { IconCross } from '@/icons/icon-cross';\r\nimport { cn, when } from '@/lib/cn';\r\nimport './input.scss';\r\n\r\nexport type InputSize = 'sm' | 'md' | 'lg';\r\n\r\n/** Нативный `size` (ширина в символах) занят размером контрола: высоты как у `Button`. */\r\nexport interface InputProps extends Omit<\r\n InputHTMLAttributes<HTMLInputElement>,\r\n 'size'\r\n> {\r\n size?: InputSize;\r\n recommended?: boolean;\r\n error?: boolean;\r\n /** Крестик справа. Появляется, только когда в поле есть значение. */\r\n clearable?: boolean;\r\n clearLabel?: string;\r\n onClear?: () => void;\r\n}\r\n\r\nconst assignRef = (\r\n ref: Ref<HTMLInputElement> | undefined,\r\n node: HTMLInputElement | null,\r\n) => {\r\n if (typeof ref === 'function') {\r\n ref(node);\r\n return;\r\n }\r\n\r\n if (ref) ref.current = node;\r\n};\r\n\r\nconst toInputValue = (value: InputHTMLAttributes<HTMLInputElement>['value']) =>\r\n value == null ? '' : String(value);\r\n\r\n/**\r\n * Прямая запись `input.value` обновляет value-tracker React, и onChange не стреляет.\r\n * Сеттер прототипа трекер не трогает.\r\n */\r\nconst emitInputValue = (input: HTMLInputElement, next: string) => {\r\n const setter = Object.getOwnPropertyDescriptor(\r\n HTMLInputElement.prototype,\r\n 'value',\r\n )?.set;\r\n\r\n setter?.call(input, next);\r\n input.dispatchEvent(new Event('input', { bubbles: true }));\r\n};\r\n\r\nexport const Input = forwardRef<HTMLInputElement, InputProps>(function Input(\r\n {\r\n type = 'text',\r\n size = 'md',\r\n recommended,\r\n error,\r\n clearable = false,\r\n clearLabel = 'Очистить',\r\n onClear,\r\n className,\r\n disabled,\r\n readOnly,\r\n value,\r\n defaultValue,\r\n onChange,\r\n ...props\r\n },\r\n ref,\r\n) {\r\n const field = useFieldControl();\r\n const reportValue = field?.reportValue;\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const isControlled = value !== undefined;\r\n const [uncontrolledValue, setUncontrolledValue] = useState(() =>\r\n toInputValue(defaultValue),\r\n );\r\n const current = isControlled ? toInputValue(value) : uncontrolledValue;\r\n const isInvalid = Boolean(error) || Boolean(field?.invalid);\r\n const describedBy = [field?.describedBy, props['aria-describedby']]\r\n .filter(part => Boolean(part))\r\n .join(' ');\r\n\r\n const setRef = useCallback(\r\n (node: HTMLInputElement | null) => {\r\n inputRef.current = node;\r\n assignRef(ref, node);\r\n },\r\n [ref],\r\n );\r\n\r\n useEffect(() => {\r\n const form = inputRef.current?.form;\r\n if (!form || isControlled) return;\r\n\r\n // reset всплывает от form вверх и до инпута не доходит\r\n const handleReset = () => {\r\n setUncontrolledValue(toInputValue(defaultValue));\r\n };\r\n\r\n form.addEventListener('reset', handleReset);\r\n return () => form.removeEventListener('reset', handleReset);\r\n }, [isControlled, defaultValue]);\r\n\r\n useEffect(() => {\r\n reportValue?.(current);\r\n }, [current, reportValue]);\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n if (!isControlled) setUncontrolledValue(event.target.value);\r\n onChange?.(event);\r\n };\r\n\r\n const handleClearMouseDown = (event: MouseEvent<HTMLButtonElement>) => {\r\n event.preventDefault();\r\n };\r\n\r\n const handleClear = () => {\r\n const input = inputRef.current;\r\n if (!input) return;\r\n\r\n emitInputValue(input, '');\r\n onClear?.();\r\n input.focus();\r\n };\r\n\r\n const canClear = clearable && !disabled && !readOnly && current !== '';\r\n\r\n return (\r\n <div className=\"input__wrap\">\r\n <input\r\n {...props}\r\n ref={setRef}\r\n type={type}\r\n disabled={disabled}\r\n readOnly={readOnly}\r\n {...(isControlled ? { value } : { defaultValue })}\r\n onChange={handleChange}\r\n {...(isInvalid ? { 'aria-invalid': true } : {})}\r\n {...(field ? { id: field.id } : {})}\r\n {...(field?.required\r\n ? { 'required': true, 'aria-required': true }\r\n : {})}\r\n {...(describedBy ? { 'aria-describedby': describedBy } : {})}\r\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\r\n className={cn(\r\n 'input',\r\n className,\r\n when(size !== 'md', `input--${size}`),\r\n when(recommended, 'input--recommended'),\r\n when(isInvalid, 'input--error'),\r\n when(clearable && !disabled && !readOnly, 'input--clearable'),\r\n )}\r\n />\r\n {canClear ? (\r\n <button\r\n type=\"button\"\r\n className=\"input__clear\"\r\n aria-label={clearLabel}\r\n onMouseDown={handleClearMouseDown}\r\n onClick={handleClear}\r\n >\r\n <IconCross width={16} height={16} />\r\n </button>\r\n ) : null}\r\n </div>\r\n );\r\n});\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"input.cjs","names":["forwardRef","useFieldControl","useRef","useState","useCallback","cn","when","IconCross"],"sources":["../../../src/components/input/input.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type InputHTMLAttributes,\n type MouseEvent,\n type Ref,\n} from 'react';\n\nimport { useFieldControl } from '@/components/field/field-context';\nimport { IconCross } from '@/icons/icon-cross';\nimport { cn, when } from '@/lib/cn';\nimport './input.scss';\n\nexport type InputSize = 'sm' | 'md' | 'lg';\n\n/** Нативный `size` (ширина в символах) занят размером контрола: высоты как у `Button`. */\nexport interface InputProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n 'size'\n> {\n size?: InputSize;\n recommended?: boolean;\n error?: boolean;\n /** Крестик справа. Появляется, только когда в поле есть значение. */\n clearable?: boolean;\n clearLabel?: string;\n onClear?: () => void;\n}\n\nconst assignRef = (\n ref: Ref<HTMLInputElement> | undefined,\n node: HTMLInputElement | null,\n) => {\n if (typeof ref === 'function') {\n ref(node);\n return;\n }\n\n if (ref) ref.current = node;\n};\n\nconst toInputValue = (value: InputHTMLAttributes<HTMLInputElement>['value']) =>\n value == null ? '' : String(value);\n\n/**\n * Прямая запись `input.value` обновляет value-tracker React, и onChange не стреляет.\n * Сеттер прототипа трекер не трогает.\n */\nconst emitInputValue = (input: HTMLInputElement, next: string) => {\n const setter = Object.getOwnPropertyDescriptor(\n HTMLInputElement.prototype,\n 'value',\n )?.set;\n\n setter?.call(input, next);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n};\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n type = 'text',\n size = 'md',\n recommended,\n error,\n clearable = false,\n clearLabel = 'Очистить',\n onClear,\n className,\n disabled,\n readOnly,\n value,\n defaultValue,\n onChange,\n ...props\n },\n ref,\n) {\n const field = useFieldControl();\n const reportValue = field?.reportValue;\n const inputRef = useRef<HTMLInputElement>(null);\n const isControlled = value !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState(() =>\n toInputValue(defaultValue),\n );\n const current = isControlled ? toInputValue(value) : uncontrolledValue;\n const isInvalid = Boolean(error) || Boolean(field?.invalid);\n const describedBy = [field?.describedBy, props['aria-describedby']]\n .filter(part => Boolean(part))\n .join(' ');\n\n const setRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n assignRef(ref, node);\n },\n [ref],\n );\n\n useEffect(() => {\n const form = inputRef.current?.form;\n if (!form || isControlled) return;\n\n // reset всплывает от form вверх и до инпута не доходит\n const handleReset = () => {\n setUncontrolledValue(toInputValue(defaultValue));\n };\n\n form.addEventListener('reset', handleReset);\n return () => form.removeEventListener('reset', handleReset);\n }, [isControlled, defaultValue]);\n\n useEffect(() => {\n reportValue?.(current);\n }, [current, reportValue]);\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n if (!isControlled) setUncontrolledValue(event.target.value);\n onChange?.(event);\n };\n\n const handleClearMouseDown = (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n };\n\n const handleClear = () => {\n const input = inputRef.current;\n if (!input) return;\n\n emitInputValue(input, '');\n onClear?.();\n input.focus();\n };\n\n const canClear = clearable && !disabled && !readOnly && current !== '';\n\n return (\n <div className=\"input__wrap\">\n <input\n {...props}\n ref={setRef}\n type={type}\n disabled={disabled}\n readOnly={readOnly}\n {...(isControlled ? { value } : { defaultValue })}\n onChange={handleChange}\n {...(isInvalid ? { 'aria-invalid': true } : {})}\n {...(field ? { id: field.id } : {})}\n {...(field?.required\n ? { 'required': true, 'aria-required': true }\n : {})}\n {...(describedBy ? { 'aria-describedby': describedBy } : {})}\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\n className={cn(\n 'input',\n className,\n when(size !== 'md', `input--${size}`),\n when(recommended, 'input--recommended'),\n when(isInvalid, 'input--error'),\n when(clearable && !disabled && !readOnly, 'input--clearable'),\n )}\n />\n {canClear ? (\n <button\n type=\"button\"\n className=\"input__clear\"\n aria-label={clearLabel}\n onMouseDown={handleClearMouseDown}\n onClick={handleClear}\n >\n <IconCross width={16} height={16} />\n </button>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"input.js","names":[],"sources":["../../../src/components/input/input.tsx"],"sourcesContent":["import {\r\n forwardRef,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n type ChangeEvent,\r\n type InputHTMLAttributes,\r\n type MouseEvent,\r\n type Ref,\r\n} from 'react';\r\n\r\nimport { useFieldControl } from '@/components/field/field-context';\r\nimport { IconCross } from '@/icons/icon-cross';\r\nimport { cn, when } from '@/lib/cn';\r\nimport './input.scss';\r\n\r\nexport type InputSize = 'sm' | 'md' | 'lg';\r\n\r\n/** Нативный `size` (ширина в символах) занят размером контрола: высоты как у `Button`. */\r\nexport interface InputProps extends Omit<\r\n InputHTMLAttributes<HTMLInputElement>,\r\n 'size'\r\n> {\r\n size?: InputSize;\r\n recommended?: boolean;\r\n error?: boolean;\r\n /** Крестик справа. Появляется, только когда в поле есть значение. */\r\n clearable?: boolean;\r\n clearLabel?: string;\r\n onClear?: () => void;\r\n}\r\n\r\nconst assignRef = (\r\n ref: Ref<HTMLInputElement> | undefined,\r\n node: HTMLInputElement | null,\r\n) => {\r\n if (typeof ref === 'function') {\r\n ref(node);\r\n return;\r\n }\r\n\r\n if (ref) ref.current = node;\r\n};\r\n\r\nconst toInputValue = (value: InputHTMLAttributes<HTMLInputElement>['value']) =>\r\n value == null ? '' : String(value);\r\n\r\n/**\r\n * Прямая запись `input.value` обновляет value-tracker React, и onChange не стреляет.\r\n * Сеттер прототипа трекер не трогает.\r\n */\r\nconst emitInputValue = (input: HTMLInputElement, next: string) => {\r\n const setter = Object.getOwnPropertyDescriptor(\r\n HTMLInputElement.prototype,\r\n 'value',\r\n )?.set;\r\n\r\n setter?.call(input, next);\r\n input.dispatchEvent(new Event('input', { bubbles: true }));\r\n};\r\n\r\nexport const Input = forwardRef<HTMLInputElement, InputProps>(function Input(\r\n {\r\n type = 'text',\r\n size = 'md',\r\n recommended,\r\n error,\r\n clearable = false,\r\n clearLabel = 'Очистить',\r\n onClear,\r\n className,\r\n disabled,\r\n readOnly,\r\n value,\r\n defaultValue,\r\n onChange,\r\n ...props\r\n },\r\n ref,\r\n) {\r\n const field = useFieldControl();\r\n const reportValue = field?.reportValue;\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const isControlled = value !== undefined;\r\n const [uncontrolledValue, setUncontrolledValue] = useState(() =>\r\n toInputValue(defaultValue),\r\n );\r\n const current = isControlled ? toInputValue(value) : uncontrolledValue;\r\n const isInvalid = Boolean(error) || Boolean(field?.invalid);\r\n const describedBy = [field?.describedBy, props['aria-describedby']]\r\n .filter(part => Boolean(part))\r\n .join(' ');\r\n\r\n const setRef = useCallback(\r\n (node: HTMLInputElement | null) => {\r\n inputRef.current = node;\r\n assignRef(ref, node);\r\n },\r\n [ref],\r\n );\r\n\r\n useEffect(() => {\r\n const form = inputRef.current?.form;\r\n if (!form || isControlled) return;\r\n\r\n // reset всплывает от form вверх и до инпута не доходит\r\n const handleReset = () => {\r\n setUncontrolledValue(toInputValue(defaultValue));\r\n };\r\n\r\n form.addEventListener('reset', handleReset);\r\n return () => form.removeEventListener('reset', handleReset);\r\n }, [isControlled, defaultValue]);\r\n\r\n useEffect(() => {\r\n reportValue?.(current);\r\n }, [current, reportValue]);\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n if (!isControlled) setUncontrolledValue(event.target.value);\r\n onChange?.(event);\r\n };\r\n\r\n const handleClearMouseDown = (event: MouseEvent<HTMLButtonElement>) => {\r\n event.preventDefault();\r\n };\r\n\r\n const handleClear = () => {\r\n const input = inputRef.current;\r\n if (!input) return;\r\n\r\n emitInputValue(input, '');\r\n onClear?.();\r\n input.focus();\r\n };\r\n\r\n const canClear = clearable && !disabled && !readOnly && current !== '';\r\n\r\n return (\r\n <div className=\"input__wrap\">\r\n <input\r\n {...props}\r\n ref={setRef}\r\n type={type}\r\n disabled={disabled}\r\n readOnly={readOnly}\r\n {...(isControlled ? { value } : { defaultValue })}\r\n onChange={handleChange}\r\n {...(isInvalid ? { 'aria-invalid': true } : {})}\r\n {...(field ? { id: field.id } : {})}\r\n {...(field?.required\r\n ? { 'required': true, 'aria-required': true }\r\n : {})}\r\n {...(describedBy ? { 'aria-describedby': describedBy } : {})}\r\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\r\n className={cn(\r\n 'input',\r\n className,\r\n when(size !== 'md', `input--${size}`),\r\n when(recommended, 'input--recommended'),\r\n when(isInvalid, 'input--error'),\r\n when(clearable && !disabled && !readOnly, 'input--clearable'),\r\n )}\r\n />\r\n {canClear ? (\r\n <button\r\n type=\"button\"\r\n className=\"input__clear\"\r\n aria-label={clearLabel}\r\n onMouseDown={handleClearMouseDown}\r\n onClick={handleClear}\r\n >\r\n <IconCross width={16} height={16} />\r\n </button>\r\n ) : null}\r\n </div>\r\n );\r\n});\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"input.js","names":[],"sources":["../../../src/components/input/input.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type InputHTMLAttributes,\n type MouseEvent,\n type Ref,\n} from 'react';\n\nimport { useFieldControl } from '@/components/field/field-context';\nimport { IconCross } from '@/icons/icon-cross';\nimport { cn, when } from '@/lib/cn';\nimport './input.scss';\n\nexport type InputSize = 'sm' | 'md' | 'lg';\n\n/** Нативный `size` (ширина в символах) занят размером контрола: высоты как у `Button`. */\nexport interface InputProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n 'size'\n> {\n size?: InputSize;\n recommended?: boolean;\n error?: boolean;\n /** Крестик справа. Появляется, только когда в поле есть значение. */\n clearable?: boolean;\n clearLabel?: string;\n onClear?: () => void;\n}\n\nconst assignRef = (\n ref: Ref<HTMLInputElement> | undefined,\n node: HTMLInputElement | null,\n) => {\n if (typeof ref === 'function') {\n ref(node);\n return;\n }\n\n if (ref) ref.current = node;\n};\n\nconst toInputValue = (value: InputHTMLAttributes<HTMLInputElement>['value']) =>\n value == null ? '' : String(value);\n\n/**\n * Прямая запись `input.value` обновляет value-tracker React, и onChange не стреляет.\n * Сеттер прототипа трекер не трогает.\n */\nconst emitInputValue = (input: HTMLInputElement, next: string) => {\n const setter = Object.getOwnPropertyDescriptor(\n HTMLInputElement.prototype,\n 'value',\n )?.set;\n\n setter?.call(input, next);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n};\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n type = 'text',\n size = 'md',\n recommended,\n error,\n clearable = false,\n clearLabel = 'Очистить',\n onClear,\n className,\n disabled,\n readOnly,\n value,\n defaultValue,\n onChange,\n ...props\n },\n ref,\n) {\n const field = useFieldControl();\n const reportValue = field?.reportValue;\n const inputRef = useRef<HTMLInputElement>(null);\n const isControlled = value !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState(() =>\n toInputValue(defaultValue),\n );\n const current = isControlled ? toInputValue(value) : uncontrolledValue;\n const isInvalid = Boolean(error) || Boolean(field?.invalid);\n const describedBy = [field?.describedBy, props['aria-describedby']]\n .filter(part => Boolean(part))\n .join(' ');\n\n const setRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n assignRef(ref, node);\n },\n [ref],\n );\n\n useEffect(() => {\n const form = inputRef.current?.form;\n if (!form || isControlled) return;\n\n // reset всплывает от form вверх и до инпута не доходит\n const handleReset = () => {\n setUncontrolledValue(toInputValue(defaultValue));\n };\n\n form.addEventListener('reset', handleReset);\n return () => form.removeEventListener('reset', handleReset);\n }, [isControlled, defaultValue]);\n\n useEffect(() => {\n reportValue?.(current);\n }, [current, reportValue]);\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n if (!isControlled) setUncontrolledValue(event.target.value);\n onChange?.(event);\n };\n\n const handleClearMouseDown = (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n };\n\n const handleClear = () => {\n const input = inputRef.current;\n if (!input) return;\n\n emitInputValue(input, '');\n onClear?.();\n input.focus();\n };\n\n const canClear = clearable && !disabled && !readOnly && current !== '';\n\n return (\n <div className=\"input__wrap\">\n <input\n {...props}\n ref={setRef}\n type={type}\n disabled={disabled}\n readOnly={readOnly}\n {...(isControlled ? { value } : { defaultValue })}\n onChange={handleChange}\n {...(isInvalid ? { 'aria-invalid': true } : {})}\n {...(field ? { id: field.id } : {})}\n {...(field?.required\n ? { 'required': true, 'aria-required': true }\n : {})}\n {...(describedBy ? { 'aria-describedby': describedBy } : {})}\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\n className={cn(\n 'input',\n className,\n when(size !== 'md', `input--${size}`),\n when(recommended, 'input--recommended'),\n when(isInvalid, 'input--error'),\n when(clearable && !disabled && !readOnly, 'input--clearable'),\n )}\n />\n {canClear ? (\n <button\n type=\"button\"\n className=\"input__clear\"\n aria-label={clearLabel}\n onMouseDown={handleClearMouseDown}\n onClick={handleClear}\n >\n <IconCross width={16} height={16} />\n </button>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}