@de-kit/st-ui 0.1.7 → 0.1.8

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 (44) hide show
  1. package/README.md +143 -143
  2. package/dist/components/button/button.cjs +4 -3
  3. package/dist/components/button/button.cjs.map +1 -1
  4. package/dist/components/button/button.d.cts +5 -3
  5. package/dist/components/button/button.d.ts +5 -3
  6. package/dist/components/button/button.js +4 -3
  7. package/dist/components/button/button.js.map +1 -1
  8. package/dist/components/button/button.scss +147 -148
  9. package/dist/components/checkbox/checkbox.cjs.map +1 -1
  10. package/dist/components/checkbox/checkbox.js.map +1 -1
  11. package/dist/components/checkbox/checkbox.scss +137 -137
  12. package/dist/components/field/field-context.cjs.map +1 -1
  13. package/dist/components/field/field-context.js.map +1 -1
  14. package/dist/components/field/field.cjs.map +1 -1
  15. package/dist/components/field/field.js.map +1 -1
  16. package/dist/components/field/field.scss +43 -43
  17. package/dist/components/input/input.cjs.map +1 -1
  18. package/dist/components/input/input.js.map +1 -1
  19. package/dist/components/input/input.scss +127 -127
  20. package/dist/components/radio/radio.cjs.map +1 -1
  21. package/dist/components/radio/radio.js.map +1 -1
  22. package/dist/components/radio/radio.scss +144 -144
  23. package/dist/components/spinner/spinner.cjs.map +1 -1
  24. package/dist/components/spinner/spinner.js.map +1 -1
  25. package/dist/components/spinner/spinner.scss +29 -29
  26. package/dist/components/textarea/textarea.cjs.map +1 -1
  27. package/dist/components/textarea/textarea.js.map +1 -1
  28. package/dist/components/textarea/textarea.scss +85 -85
  29. package/dist/icons/icon-label-ozon.cjs +4 -4
  30. package/dist/icons/icon-label-ozon.cjs.map +1 -1
  31. package/dist/icons/icon-label-ozon.js +4 -4
  32. package/dist/icons/icon-label-ozon.js.map +1 -1
  33. package/dist/icons/icon-label-yandex.cjs +1 -1
  34. package/dist/icons/icon-label-yandex.cjs.map +1 -1
  35. package/dist/icons/icon-label-yandex.js +1 -1
  36. package/dist/icons/icon-label-yandex.js.map +1 -1
  37. package/dist/icons/svg.cjs.map +1 -1
  38. package/dist/icons/svg.js.map +1 -1
  39. package/dist/lib/cn.cjs.map +1 -1
  40. package/dist/lib/cn.js.map +1 -1
  41. package/dist/styles/global.scss +201 -201
  42. package/dist/styles/mixins.scss +51 -51
  43. package/dist/styles/vars.scss +87 -87
  44. package/package.json +1 -1
@@ -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 {\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
+ {"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 +1 @@
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
+ {"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,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 {\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
+ {"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 +1 @@
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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"}
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,127 +1,127 @@
1
- .input {
2
- // Размер контрола. Высоты совпадают с Button: sm 30px, md 40px, lg 52px.
3
- --input-padding-y: 9px;
4
- --input-padding-x: 14px;
5
- --input-height: 40px;
6
- box-sizing: border-box;
7
- display: flex;
8
- align-items: center;
9
- width: 100%;
10
- padding: var(--input-padding-y) var(--input-padding-x);
11
- font-size: var(--text-sm);
12
- line-height: var(--text-sm--line-height);
13
- background-color: transparent;
14
- color: var(--color-foreground);
15
- border-radius: var(--radius-sm);
16
- appearance: none;
17
- font-family: var(--font-sans);
18
- border: 1px solid var(--color-input);
19
- outline: none;
20
- transition:
21
- background 0.3s,
22
- border-color 0.3s,
23
- box-shadow 0.2s;
24
-
25
- // Фон автозаполнения Chrome перебивается только внутренней тенью.
26
- &:-webkit-autofill,
27
- &:-webkit-autofill:hover,
28
- &:-webkit-autofill:active {
29
- -webkit-box-shadow: 0 0 0 30px var(--color-background) inset;
30
- }
31
-
32
- // :-webkit-autofill:focus специфичнее :focus и затирает его box-shadow,
33
- // поэтому обводка фокуса повторяется здесь второй тенью.
34
- &:-webkit-autofill:focus {
35
- -webkit-box-shadow:
36
- 0 0 0 30px var(--color-background) inset,
37
- 0 0 0 var(--focus-ring-width) var(--focus-ring-color);
38
- }
39
-
40
- &[type='number']::-webkit-outer-spin-button,
41
- &[type='number']::-webkit-inner-spin-button {
42
- -webkit-appearance: none;
43
- margin: 0;
44
- }
45
-
46
- &[type='number'] {
47
- -moz-appearance: textfield;
48
- }
49
-
50
- &[type='date'] {
51
- height: var(--input-height);
52
- }
53
-
54
- &::-webkit-date-and-time-value {
55
- text-align: left;
56
- }
57
-
58
- &:focus {
59
- border-color: var(--color-ring);
60
- box-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring-color);
61
- }
62
-
63
- &:disabled {
64
- background-color: var(--color-muted);
65
- color: var(--color-muted-foreground);
66
- fill: var(--color-muted-foreground);
67
- }
68
-
69
- &--sm {
70
- --input-padding-y: 4px;
71
- --input-padding-x: 12px;
72
- --input-height: 30px;
73
- }
74
-
75
- &--lg {
76
- --input-padding-y: var(--space-3);
77
- --input-padding-x: 24px;
78
- --input-height: 52px;
79
- }
80
-
81
- &--recommended {
82
- background-color: var(--color-recommended);
83
- }
84
-
85
- &--error {
86
- border-color: color-mix(in srgb, var(--color-destructive) 50%, transparent);
87
- }
88
-
89
- &--clearable {
90
- padding-right: 36px;
91
-
92
- &[type='search']::-webkit-search-cancel-button {
93
- -webkit-appearance: none;
94
- appearance: none;
95
- }
96
- }
97
- }
98
-
99
- .input__wrap {
100
- position: relative;
101
- width: 100%;
102
- }
103
-
104
- .input__clear {
105
- position: absolute;
106
- top: 0;
107
- right: 4px;
108
- bottom: 0;
109
- display: flex;
110
- align-items: center;
111
- justify-content: center;
112
- width: 28px;
113
- margin: 0;
114
- padding: 0;
115
- border: 0;
116
- background: none;
117
- color: var(--color-muted-foreground);
118
-
119
- &:hover {
120
- color: var(--color-foreground);
121
- }
122
-
123
- &:focus-visible {
124
- outline: var(--focus-ring-width) solid var(--focus-ring-color);
125
- border-radius: var(--radius-sm);
126
- }
127
- }
1
+ .input {
2
+ // Размер контрола. Высоты совпадают с Button: sm 30px, md 40px, lg 52px.
3
+ --input-padding-y: 9px;
4
+ --input-padding-x: 14px;
5
+ --input-height: 40px;
6
+ box-sizing: border-box;
7
+ display: flex;
8
+ align-items: center;
9
+ width: 100%;
10
+ padding: var(--input-padding-y) var(--input-padding-x);
11
+ font-size: var(--text-sm);
12
+ line-height: var(--text-sm--line-height);
13
+ background-color: transparent;
14
+ color: var(--color-foreground);
15
+ border-radius: var(--radius-sm);
16
+ appearance: none;
17
+ font-family: var(--font-sans);
18
+ border: 1px solid var(--color-input);
19
+ outline: none;
20
+ transition:
21
+ background 0.3s,
22
+ border-color 0.3s,
23
+ box-shadow 0.2s;
24
+
25
+ // Фон автозаполнения Chrome перебивается только внутренней тенью.
26
+ &:-webkit-autofill,
27
+ &:-webkit-autofill:hover,
28
+ &:-webkit-autofill:active {
29
+ -webkit-box-shadow: 0 0 0 30px var(--color-background) inset;
30
+ }
31
+
32
+ // :-webkit-autofill:focus специфичнее :focus и затирает его box-shadow,
33
+ // поэтому обводка фокуса повторяется здесь второй тенью.
34
+ &:-webkit-autofill:focus {
35
+ -webkit-box-shadow:
36
+ 0 0 0 30px var(--color-background) inset,
37
+ 0 0 0 var(--focus-ring-width) var(--focus-ring-color);
38
+ }
39
+
40
+ &[type='number']::-webkit-outer-spin-button,
41
+ &[type='number']::-webkit-inner-spin-button {
42
+ -webkit-appearance: none;
43
+ margin: 0;
44
+ }
45
+
46
+ &[type='number'] {
47
+ -moz-appearance: textfield;
48
+ }
49
+
50
+ &[type='date'] {
51
+ height: var(--input-height);
52
+ }
53
+
54
+ &::-webkit-date-and-time-value {
55
+ text-align: left;
56
+ }
57
+
58
+ &:focus {
59
+ border-color: var(--color-ring);
60
+ box-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring-color);
61
+ }
62
+
63
+ &:disabled {
64
+ background-color: var(--color-muted);
65
+ color: var(--color-muted-foreground);
66
+ fill: var(--color-muted-foreground);
67
+ }
68
+
69
+ &--sm {
70
+ --input-padding-y: 4px;
71
+ --input-padding-x: 12px;
72
+ --input-height: 30px;
73
+ }
74
+
75
+ &--lg {
76
+ --input-padding-y: var(--space-3);
77
+ --input-padding-x: 24px;
78
+ --input-height: 52px;
79
+ }
80
+
81
+ &--recommended {
82
+ background-color: var(--color-recommended);
83
+ }
84
+
85
+ &--error {
86
+ border-color: color-mix(in srgb, var(--color-destructive) 50%, transparent);
87
+ }
88
+
89
+ &--clearable {
90
+ padding-right: 36px;
91
+
92
+ &[type='search']::-webkit-search-cancel-button {
93
+ -webkit-appearance: none;
94
+ appearance: none;
95
+ }
96
+ }
97
+ }
98
+
99
+ .input__wrap {
100
+ position: relative;
101
+ width: 100%;
102
+ }
103
+
104
+ .input__clear {
105
+ position: absolute;
106
+ top: 0;
107
+ right: 4px;
108
+ bottom: 0;
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ width: 28px;
113
+ margin: 0;
114
+ padding: 0;
115
+ border: 0;
116
+ background: none;
117
+ color: var(--color-muted-foreground);
118
+
119
+ &:hover {
120
+ color: var(--color-foreground);
121
+ }
122
+
123
+ &:focus-visible {
124
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
125
+ border-radius: var(--radius-sm);
126
+ }
127
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"radio.cjs","names":["createContext","useFieldControl","useMemo","cn","forwardRef","useContext","useId","when"],"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n useContext,\n useId,\n useMemo,\n type ChangeEvent,\n type InputHTMLAttributes,\n type ReactNode,\n} from 'react';\n\nimport { useFieldControl } from '@/components/field/field-context';\nimport { cn, when } from '@/lib/cn';\nimport './radio.scss';\n\ninterface RadioGroupState {\n name: string;\n required: boolean;\n reportValue?: (value: string) => void;\n}\n\nconst RadioGroupStateContext = createContext<RadioGroupState | null>(null);\n\nexport interface RadioGroupProps {\n /** Легенда группы. Внутри `Field` не нужна: именем станет лейбл поля. */\n label?: ReactNode;\n name: string;\n /** Внутри `Field` его `required` и так включает группу, здесь — для группы отдельно. */\n required?: boolean;\n children: ReactNode;\n className?: string;\n}\n\nexport const RadioGroup = ({\n label,\n name,\n required: requiredProp = false,\n children,\n className,\n}: RadioGroupProps) => {\n const field = useFieldControl();\n const required = requiredProp || Boolean(field?.required);\n const labelledBy = !label && field?.labelId ? field.labelId : undefined;\n // Обязательность держат пункты: нативный required на них не даёт отправить\n // форму, а его invalid Field ловит и показывает свою ошибку. Выбор отдаём\n // в reportValue — иначе показанная ошибка так и висела бы после выбора.\n const reportValue = field?.reportValue;\n const group = useMemo<RadioGroupState>(\n () => ({ name, required, reportValue }),\n [name, required, reportValue],\n );\n\n return (\n <fieldset\n className={cn('radio-group', className)}\n role=\"radiogroup\"\n {...(labelledBy ? { 'aria-labelledby': labelledBy } : {})}\n {...(field?.invalid ? { 'aria-invalid': true } : {})}\n {...(required ? { 'aria-required': true } : {})}\n {...(field?.describedBy ? { 'aria-describedby': field.describedBy } : {})}\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\n >\n {label ? <legend className=\"radio-group__legend\">{label}</legend> : null}\n <RadioGroupStateContext.Provider value={group}>\n {children}\n </RadioGroupStateContext.Provider>\n </fieldset>\n );\n};\n\nexport interface RadioInputProps extends InputHTMLAttributes<HTMLInputElement> {\n label?: ReactNode;\n error?: boolean;\n}\n\nexport const RadioInput = forwardRef<HTMLInputElement, RadioInputProps>(\n function RadioInput(\n { id, className, label, error, disabled, name, onChange, ...props },\n ref,\n ) {\n const group = useContext(RadioGroupStateContext);\n const autoId = useId();\n const inputId = id ?? autoId;\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.checked) group?.reportValue?.(event.target.value);\n onChange?.(event);\n };\n\n return (\n <div className={cn('radio', className, when(error, 'radio--error'))}>\n <input\n {...props}\n ref={ref}\n type=\"radio\"\n id={inputId}\n // Внутри группы имя группы побеждает, иначе её можно разрезать name на пункте.\n name={group?.name ?? name}\n disabled={disabled}\n // required на любом пункте делает обязательной всю группу с этим name.\n {...(group?.required ? { required: true } : {})}\n onChange={handleChange}\n className=\"radio__input\"\n />\n {label ? (\n <label className=\"radio__label\" htmlFor={inputId}>\n {label}\n </label>\n ) : null}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;AAqBA,MAAM,0BAAA,GAAyBA,MAAAA,cAAAA,CAAsC,IAAI;AAYzE,MAAa,cAAc,EACzB,OACA,MACA,UAAU,eAAe,OACzB,UACA,gBACqB;CACrB,MAAM,QAAQC,sBAAAA,gBAAgB;CAC9B,MAAM,WAAW,gBAAgB,QAAQ,OAAO,QAAQ;CACxD,MAAM,aAAa,CAAC,SAAS,OAAO,UAAU,MAAM,UAAU,KAAA;CAI9D,MAAM,cAAc,OAAO;CAC3B,MAAM,SAAA,GAAQC,MAAAA,QAAAA,QACL;EAAE;EAAM;EAAU;CAAY,IACrC;EAAC;EAAM;EAAU;CAAW,CAC9B;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,YAAD;EACE,WAAWC,WAAAA,GAAG,eAAe,SAAS;EACtC,MAAK;EACL,GAAK,aAAa,EAAE,mBAAmB,WAAW,IAAI,CAAC;EACvD,GAAK,OAAO,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;EAClD,GAAK,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAC;EAC7C,GAAK,OAAO,cAAc,EAAE,oBAAoB,MAAM,YAAY,IAAI,CAAC;EACvE,GAAK,OAAO,UAAU,EAAE,qBAAqB,MAAM,QAAQ,IAAI,CAAC;EAPlE,UAAA,CASG,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GAAQ,WAAU;GAAuB,UAAA;EAAc,CAAA,IAAI,MACpE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAuB,UAAxB;GAAiC,OAAO;GACrC;EAC8B,CAAA,CACzB;;AAEd;AAOA,MAAa,cAAA,GAAaC,MAAAA,WAAAA,CACxB,SAAS,WACP,EAAE,IAAI,WAAW,OAAO,OAAO,UAAU,MAAM,UAAU,GAAG,SAC5D,KACA;CACA,MAAM,SAAA,GAAQC,MAAAA,WAAAA,CAAW,sBAAsB;CAC/C,MAAM,UAAA,GAASC,MAAAA,MAAAA,CAAM;CACrB,MAAM,UAAU,MAAM;CAEtB,MAAM,gBAAgB,UAAyC;EAC7D,IAAI,MAAM,OAAO,SAAS,OAAO,cAAc,MAAM,OAAO,KAAK;EACjE,WAAW,KAAK;CAClB;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAWH,WAAAA,GAAG,SAAS,WAAWI,WAAAA,KAAK,OAAO,cAAc,CAAC;EAAlE,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;GACE,GAAI;GACC;GACL,MAAK;GACL,IAAI;GAEJ,MAAM,OAAO,QAAQ;GACX;GAEV,GAAK,OAAO,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;GAC7C,UAAU;GACV,WAAU;EACX,CAAA,GACA,QACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;GAAO,WAAU;GAAe,SAAS;GACtC,UAAA;EACI,CAAA,IACL,IACD;;AAET,CACF"}
1
+ {"version":3,"file":"radio.cjs","names":["createContext","useFieldControl","useMemo","cn","forwardRef","useContext","useId","when"],"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["import {\r\n createContext,\r\n forwardRef,\r\n useContext,\r\n useId,\r\n useMemo,\r\n type ChangeEvent,\r\n type InputHTMLAttributes,\r\n type ReactNode,\r\n} from 'react';\r\n\r\nimport { useFieldControl } from '@/components/field/field-context';\r\nimport { cn, when } from '@/lib/cn';\r\nimport './radio.scss';\r\n\r\ninterface RadioGroupState {\r\n name: string;\r\n required: boolean;\r\n reportValue?: (value: string) => void;\r\n}\r\n\r\nconst RadioGroupStateContext = createContext<RadioGroupState | null>(null);\r\n\r\nexport interface RadioGroupProps {\r\n /** Легенда группы. Внутри `Field` не нужна: именем станет лейбл поля. */\r\n label?: ReactNode;\r\n name: string;\r\n /** Внутри `Field` его `required` и так включает группу, здесь — для группы отдельно. */\r\n required?: boolean;\r\n children: ReactNode;\r\n className?: string;\r\n}\r\n\r\nexport const RadioGroup = ({\r\n label,\r\n name,\r\n required: requiredProp = false,\r\n children,\r\n className,\r\n}: RadioGroupProps) => {\r\n const field = useFieldControl();\r\n const required = requiredProp || Boolean(field?.required);\r\n const labelledBy = !label && field?.labelId ? field.labelId : undefined;\r\n // Обязательность держат пункты: нативный required на них не даёт отправить\r\n // форму, а его invalid Field ловит и показывает свою ошибку. Выбор отдаём\r\n // в reportValue — иначе показанная ошибка так и висела бы после выбора.\r\n const reportValue = field?.reportValue;\r\n const group = useMemo<RadioGroupState>(\r\n () => ({ name, required, reportValue }),\r\n [name, required, reportValue],\r\n );\r\n\r\n return (\r\n <fieldset\r\n className={cn('radio-group', className)}\r\n role=\"radiogroup\"\r\n {...(labelledBy ? { 'aria-labelledby': labelledBy } : {})}\r\n {...(field?.invalid ? { 'aria-invalid': true } : {})}\r\n {...(required ? { 'aria-required': true } : {})}\r\n {...(field?.describedBy ? { 'aria-describedby': field.describedBy } : {})}\r\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\r\n >\r\n {label ? <legend className=\"radio-group__legend\">{label}</legend> : null}\r\n <RadioGroupStateContext.Provider value={group}>\r\n {children}\r\n </RadioGroupStateContext.Provider>\r\n </fieldset>\r\n );\r\n};\r\n\r\nexport interface RadioInputProps extends InputHTMLAttributes<HTMLInputElement> {\r\n label?: ReactNode;\r\n error?: boolean;\r\n}\r\n\r\nexport const RadioInput = forwardRef<HTMLInputElement, RadioInputProps>(\r\n function RadioInput(\r\n { id, className, label, error, disabled, name, onChange, ...props },\r\n ref,\r\n ) {\r\n const group = useContext(RadioGroupStateContext);\r\n const autoId = useId();\r\n const inputId = id ?? autoId;\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n if (event.target.checked) group?.reportValue?.(event.target.value);\r\n onChange?.(event);\r\n };\r\n\r\n return (\r\n <div className={cn('radio', className, when(error, 'radio--error'))}>\r\n <input\r\n {...props}\r\n ref={ref}\r\n type=\"radio\"\r\n id={inputId}\r\n // Внутри группы имя группы побеждает, иначе её можно разрезать name на пункте.\r\n name={group?.name ?? name}\r\n disabled={disabled}\r\n // required на любом пункте делает обязательной всю группу с этим name.\r\n {...(group?.required ? { required: true } : {})}\r\n onChange={handleChange}\r\n className=\"radio__input\"\r\n />\r\n {label ? (\r\n <label className=\"radio__label\" htmlFor={inputId}>\r\n {label}\r\n </label>\r\n ) : null}\r\n </div>\r\n );\r\n },\r\n);\r\n"],"mappings":";;;;;;AAqBA,MAAM,0BAAA,GAAyBA,MAAAA,cAAAA,CAAsC,IAAI;AAYzE,MAAa,cAAc,EACzB,OACA,MACA,UAAU,eAAe,OACzB,UACA,gBACqB;CACrB,MAAM,QAAQC,sBAAAA,gBAAgB;CAC9B,MAAM,WAAW,gBAAgB,QAAQ,OAAO,QAAQ;CACxD,MAAM,aAAa,CAAC,SAAS,OAAO,UAAU,MAAM,UAAU,KAAA;CAI9D,MAAM,cAAc,OAAO;CAC3B,MAAM,SAAA,GAAQC,MAAAA,QAAAA,QACL;EAAE;EAAM;EAAU;CAAY,IACrC;EAAC;EAAM;EAAU;CAAW,CAC9B;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,YAAD;EACE,WAAWC,WAAAA,GAAG,eAAe,SAAS;EACtC,MAAK;EACL,GAAK,aAAa,EAAE,mBAAmB,WAAW,IAAI,CAAC;EACvD,GAAK,OAAO,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;EAClD,GAAK,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAC;EAC7C,GAAK,OAAO,cAAc,EAAE,oBAAoB,MAAM,YAAY,IAAI,CAAC;EACvE,GAAK,OAAO,UAAU,EAAE,qBAAqB,MAAM,QAAQ,IAAI,CAAC;EAPlE,UAAA,CASG,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GAAQ,WAAU;GAAuB,UAAA;EAAc,CAAA,IAAI,MACpE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAuB,UAAxB;GAAiC,OAAO;GACrC;EAC8B,CAAA,CACzB;;AAEd;AAOA,MAAa,cAAA,GAAaC,MAAAA,WAAAA,CACxB,SAAS,WACP,EAAE,IAAI,WAAW,OAAO,OAAO,UAAU,MAAM,UAAU,GAAG,SAC5D,KACA;CACA,MAAM,SAAA,GAAQC,MAAAA,WAAAA,CAAW,sBAAsB;CAC/C,MAAM,UAAA,GAASC,MAAAA,MAAAA,CAAM;CACrB,MAAM,UAAU,MAAM;CAEtB,MAAM,gBAAgB,UAAyC;EAC7D,IAAI,MAAM,OAAO,SAAS,OAAO,cAAc,MAAM,OAAO,KAAK;EACjE,WAAW,KAAK;CAClB;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAWH,WAAAA,GAAG,SAAS,WAAWI,WAAAA,KAAK,OAAO,cAAc,CAAC;EAAlE,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;GACE,GAAI;GACC;GACL,MAAK;GACL,IAAI;GAEJ,MAAM,OAAO,QAAQ;GACX;GAEV,GAAK,OAAO,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;GAC7C,UAAU;GACV,WAAU;EACX,CAAA,GACA,QACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;GAAO,WAAU;GAAe,SAAS;GACtC,UAAA;EACI,CAAA,IACL,IACD;;AAET,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"radio.js","names":[],"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n useContext,\n useId,\n useMemo,\n type ChangeEvent,\n type InputHTMLAttributes,\n type ReactNode,\n} from 'react';\n\nimport { useFieldControl } from '@/components/field/field-context';\nimport { cn, when } from '@/lib/cn';\nimport './radio.scss';\n\ninterface RadioGroupState {\n name: string;\n required: boolean;\n reportValue?: (value: string) => void;\n}\n\nconst RadioGroupStateContext = createContext<RadioGroupState | null>(null);\n\nexport interface RadioGroupProps {\n /** Легенда группы. Внутри `Field` не нужна: именем станет лейбл поля. */\n label?: ReactNode;\n name: string;\n /** Внутри `Field` его `required` и так включает группу, здесь — для группы отдельно. */\n required?: boolean;\n children: ReactNode;\n className?: string;\n}\n\nexport const RadioGroup = ({\n label,\n name,\n required: requiredProp = false,\n children,\n className,\n}: RadioGroupProps) => {\n const field = useFieldControl();\n const required = requiredProp || Boolean(field?.required);\n const labelledBy = !label && field?.labelId ? field.labelId : undefined;\n // Обязательность держат пункты: нативный required на них не даёт отправить\n // форму, а его invalid Field ловит и показывает свою ошибку. Выбор отдаём\n // в reportValue — иначе показанная ошибка так и висела бы после выбора.\n const reportValue = field?.reportValue;\n const group = useMemo<RadioGroupState>(\n () => ({ name, required, reportValue }),\n [name, required, reportValue],\n );\n\n return (\n <fieldset\n className={cn('radio-group', className)}\n role=\"radiogroup\"\n {...(labelledBy ? { 'aria-labelledby': labelledBy } : {})}\n {...(field?.invalid ? { 'aria-invalid': true } : {})}\n {...(required ? { 'aria-required': true } : {})}\n {...(field?.describedBy ? { 'aria-describedby': field.describedBy } : {})}\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\n >\n {label ? <legend className=\"radio-group__legend\">{label}</legend> : null}\n <RadioGroupStateContext.Provider value={group}>\n {children}\n </RadioGroupStateContext.Provider>\n </fieldset>\n );\n};\n\nexport interface RadioInputProps extends InputHTMLAttributes<HTMLInputElement> {\n label?: ReactNode;\n error?: boolean;\n}\n\nexport const RadioInput = forwardRef<HTMLInputElement, RadioInputProps>(\n function RadioInput(\n { id, className, label, error, disabled, name, onChange, ...props },\n ref,\n ) {\n const group = useContext(RadioGroupStateContext);\n const autoId = useId();\n const inputId = id ?? autoId;\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.checked) group?.reportValue?.(event.target.value);\n onChange?.(event);\n };\n\n return (\n <div className={cn('radio', className, when(error, 'radio--error'))}>\n <input\n {...props}\n ref={ref}\n type=\"radio\"\n id={inputId}\n // Внутри группы имя группы побеждает, иначе её можно разрезать name на пункте.\n name={group?.name ?? name}\n disabled={disabled}\n // required на любом пункте делает обязательной всю группу с этим name.\n {...(group?.required ? { required: true } : {})}\n onChange={handleChange}\n className=\"radio__input\"\n />\n {label ? (\n <label className=\"radio__label\" htmlFor={inputId}>\n {label}\n </label>\n ) : null}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;AAqBA,MAAM,yBAAyB,cAAsC,IAAI;AAYzE,MAAa,cAAc,EACzB,OACA,MACA,UAAU,eAAe,OACzB,UACA,gBACqB;CACrB,MAAM,QAAQ,gBAAgB;CAC9B,MAAM,WAAW,gBAAgB,QAAQ,OAAO,QAAQ;CACxD,MAAM,aAAa,CAAC,SAAS,OAAO,UAAU,MAAM,UAAU,KAAA;CAI9D,MAAM,cAAc,OAAO;CAC3B,MAAM,QAAQ,eACL;EAAE;EAAM;EAAU;CAAY,IACrC;EAAC;EAAM;EAAU;CAAW,CAC9B;CAEA,OACE,qBAAC,YAAD;EACE,WAAW,GAAG,eAAe,SAAS;EACtC,MAAK;EACL,GAAK,aAAa,EAAE,mBAAmB,WAAW,IAAI,CAAC;EACvD,GAAK,OAAO,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;EAClD,GAAK,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAC;EAC7C,GAAK,OAAO,cAAc,EAAE,oBAAoB,MAAM,YAAY,IAAI,CAAC;EACvE,GAAK,OAAO,UAAU,EAAE,qBAAqB,MAAM,QAAQ,IAAI,CAAC;EAPlE,UAAA,CASG,QAAQ,oBAAC,UAAD;GAAQ,WAAU;GAAuB,UAAA;EAAc,CAAA,IAAI,MACpE,oBAAC,uBAAuB,UAAxB;GAAiC,OAAO;GACrC;EAC8B,CAAA,CACzB;;AAEd;AAOA,MAAa,aAAa,WACxB,SAAS,WACP,EAAE,IAAI,WAAW,OAAO,OAAO,UAAU,MAAM,UAAU,GAAG,SAC5D,KACA;CACA,MAAM,QAAQ,WAAW,sBAAsB;CAC/C,MAAM,SAAS,MAAM;CACrB,MAAM,UAAU,MAAM;CAEtB,MAAM,gBAAgB,UAAyC;EAC7D,IAAI,MAAM,OAAO,SAAS,OAAO,cAAc,MAAM,OAAO,KAAK;EACjE,WAAW,KAAK;CAClB;CAEA,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,SAAS,WAAW,KAAK,OAAO,cAAc,CAAC;EAAlE,UAAA,CACE,oBAAC,SAAD;GACE,GAAI;GACC;GACL,MAAK;GACL,IAAI;GAEJ,MAAM,OAAO,QAAQ;GACX;GAEV,GAAK,OAAO,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;GAC7C,UAAU;GACV,WAAU;EACX,CAAA,GACA,QACC,oBAAC,SAAD;GAAO,WAAU;GAAe,SAAS;GACtC,UAAA;EACI,CAAA,IACL,IACD;;AAET,CACF"}
1
+ {"version":3,"file":"radio.js","names":[],"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["import {\r\n createContext,\r\n forwardRef,\r\n useContext,\r\n useId,\r\n useMemo,\r\n type ChangeEvent,\r\n type InputHTMLAttributes,\r\n type ReactNode,\r\n} from 'react';\r\n\r\nimport { useFieldControl } from '@/components/field/field-context';\r\nimport { cn, when } from '@/lib/cn';\r\nimport './radio.scss';\r\n\r\ninterface RadioGroupState {\r\n name: string;\r\n required: boolean;\r\n reportValue?: (value: string) => void;\r\n}\r\n\r\nconst RadioGroupStateContext = createContext<RadioGroupState | null>(null);\r\n\r\nexport interface RadioGroupProps {\r\n /** Легенда группы. Внутри `Field` не нужна: именем станет лейбл поля. */\r\n label?: ReactNode;\r\n name: string;\r\n /** Внутри `Field` его `required` и так включает группу, здесь — для группы отдельно. */\r\n required?: boolean;\r\n children: ReactNode;\r\n className?: string;\r\n}\r\n\r\nexport const RadioGroup = ({\r\n label,\r\n name,\r\n required: requiredProp = false,\r\n children,\r\n className,\r\n}: RadioGroupProps) => {\r\n const field = useFieldControl();\r\n const required = requiredProp || Boolean(field?.required);\r\n const labelledBy = !label && field?.labelId ? field.labelId : undefined;\r\n // Обязательность держат пункты: нативный required на них не даёт отправить\r\n // форму, а его invalid Field ловит и показывает свою ошибку. Выбор отдаём\r\n // в reportValue — иначе показанная ошибка так и висела бы после выбора.\r\n const reportValue = field?.reportValue;\r\n const group = useMemo<RadioGroupState>(\r\n () => ({ name, required, reportValue }),\r\n [name, required, reportValue],\r\n );\r\n\r\n return (\r\n <fieldset\r\n className={cn('radio-group', className)}\r\n role=\"radiogroup\"\r\n {...(labelledBy ? { 'aria-labelledby': labelledBy } : {})}\r\n {...(field?.invalid ? { 'aria-invalid': true } : {})}\r\n {...(required ? { 'aria-required': true } : {})}\r\n {...(field?.describedBy ? { 'aria-describedby': field.describedBy } : {})}\r\n {...(field?.errorId ? { 'aria-errormessage': field.errorId } : {})}\r\n >\r\n {label ? <legend className=\"radio-group__legend\">{label}</legend> : null}\r\n <RadioGroupStateContext.Provider value={group}>\r\n {children}\r\n </RadioGroupStateContext.Provider>\r\n </fieldset>\r\n );\r\n};\r\n\r\nexport interface RadioInputProps extends InputHTMLAttributes<HTMLInputElement> {\r\n label?: ReactNode;\r\n error?: boolean;\r\n}\r\n\r\nexport const RadioInput = forwardRef<HTMLInputElement, RadioInputProps>(\r\n function RadioInput(\r\n { id, className, label, error, disabled, name, onChange, ...props },\r\n ref,\r\n ) {\r\n const group = useContext(RadioGroupStateContext);\r\n const autoId = useId();\r\n const inputId = id ?? autoId;\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n if (event.target.checked) group?.reportValue?.(event.target.value);\r\n onChange?.(event);\r\n };\r\n\r\n return (\r\n <div className={cn('radio', className, when(error, 'radio--error'))}>\r\n <input\r\n {...props}\r\n ref={ref}\r\n type=\"radio\"\r\n id={inputId}\r\n // Внутри группы имя группы побеждает, иначе её можно разрезать name на пункте.\r\n name={group?.name ?? name}\r\n disabled={disabled}\r\n // required на любом пункте делает обязательной всю группу с этим name.\r\n {...(group?.required ? { required: true } : {})}\r\n onChange={handleChange}\r\n className=\"radio__input\"\r\n />\r\n {label ? (\r\n <label className=\"radio__label\" htmlFor={inputId}>\r\n {label}\r\n </label>\r\n ) : null}\r\n </div>\r\n );\r\n },\r\n);\r\n"],"mappings":";;;;;;AAqBA,MAAM,yBAAyB,cAAsC,IAAI;AAYzE,MAAa,cAAc,EACzB,OACA,MACA,UAAU,eAAe,OACzB,UACA,gBACqB;CACrB,MAAM,QAAQ,gBAAgB;CAC9B,MAAM,WAAW,gBAAgB,QAAQ,OAAO,QAAQ;CACxD,MAAM,aAAa,CAAC,SAAS,OAAO,UAAU,MAAM,UAAU,KAAA;CAI9D,MAAM,cAAc,OAAO;CAC3B,MAAM,QAAQ,eACL;EAAE;EAAM;EAAU;CAAY,IACrC;EAAC;EAAM;EAAU;CAAW,CAC9B;CAEA,OACE,qBAAC,YAAD;EACE,WAAW,GAAG,eAAe,SAAS;EACtC,MAAK;EACL,GAAK,aAAa,EAAE,mBAAmB,WAAW,IAAI,CAAC;EACvD,GAAK,OAAO,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;EAClD,GAAK,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAC;EAC7C,GAAK,OAAO,cAAc,EAAE,oBAAoB,MAAM,YAAY,IAAI,CAAC;EACvE,GAAK,OAAO,UAAU,EAAE,qBAAqB,MAAM,QAAQ,IAAI,CAAC;EAPlE,UAAA,CASG,QAAQ,oBAAC,UAAD;GAAQ,WAAU;GAAuB,UAAA;EAAc,CAAA,IAAI,MACpE,oBAAC,uBAAuB,UAAxB;GAAiC,OAAO;GACrC;EAC8B,CAAA,CACzB;;AAEd;AAOA,MAAa,aAAa,WACxB,SAAS,WACP,EAAE,IAAI,WAAW,OAAO,OAAO,UAAU,MAAM,UAAU,GAAG,SAC5D,KACA;CACA,MAAM,QAAQ,WAAW,sBAAsB;CAC/C,MAAM,SAAS,MAAM;CACrB,MAAM,UAAU,MAAM;CAEtB,MAAM,gBAAgB,UAAyC;EAC7D,IAAI,MAAM,OAAO,SAAS,OAAO,cAAc,MAAM,OAAO,KAAK;EACjE,WAAW,KAAK;CAClB;CAEA,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,SAAS,WAAW,KAAK,OAAO,cAAc,CAAC;EAAlE,UAAA,CACE,oBAAC,SAAD;GACE,GAAI;GACC;GACL,MAAK;GACL,IAAI;GAEJ,MAAM,OAAO,QAAQ;GACX;GAEV,GAAK,OAAO,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;GAC7C,UAAU;GACV,WAAU;EACX,CAAA,GACA,QACC,oBAAC,SAAD;GAAO,WAAU;GAAe,SAAS;GACtC,UAAA;EACI,CAAA,IACL,IACD;;AAET,CACF"}