@cloud-ru/ds-fields 2.2.7-preview-d50a3da1.0 → 2.2.7-preview-e30f4f90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +0 -1
  2. package/dist/cjs/components/FieldColor/FieldColor.js +1 -1
  3. package/dist/cjs/components/FieldColor/styles.module.css +9 -9
  4. package/dist/cjs/components/FieldCombo/FieldCombo.js +1 -1
  5. package/dist/cjs/components/FieldDate/styles.module.css +3 -3
  6. package/dist/cjs/components/FieldSecure/FieldSecure.js +1 -1
  7. package/dist/cjs/components/FieldSelect/FieldSelect.js +2 -2
  8. package/dist/cjs/components/FieldSelect/styles.module.css +18 -15
  9. package/dist/cjs/components/FieldSelect/types.d.ts +1 -1
  10. package/dist/cjs/components/FieldSlider/FieldSlider.js +1 -1
  11. package/dist/cjs/components/FieldSlider/styles.module.css +7 -0
  12. package/dist/cjs/components/FieldStepper/FieldStepper.js +2 -2
  13. package/dist/cjs/components/FieldStepper/styles.module.css +52 -6
  14. package/dist/cjs/components/FieldTextArea/FieldTextArea.js +1 -1
  15. package/dist/cjs/components/FieldTextArea/styles.module.css +36 -39
  16. package/dist/cjs/components/FieldTime/FieldTime.js +1 -1
  17. package/dist/cjs/components/shared/helperComponents/FieldElementButton/styles.module.css +22 -22
  18. package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.d.ts +1 -3
  19. package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.js +2 -2
  20. package/dist/cjs/components/shared/styles.module.css +124 -151
  21. package/dist/cjs/style.css +271 -245
  22. package/dist/esm/components/FieldColor/FieldColor.js +1 -1
  23. package/dist/esm/components/FieldColor/styles.module.css +9 -9
  24. package/dist/esm/components/FieldCombo/FieldCombo.js +1 -1
  25. package/dist/esm/components/FieldDate/styles.module.css +3 -3
  26. package/dist/esm/components/FieldSecure/FieldSecure.js +1 -1
  27. package/dist/esm/components/FieldSelect/FieldSelect.js +2 -2
  28. package/dist/esm/components/FieldSelect/styles.module.css +18 -15
  29. package/dist/esm/components/FieldSelect/types.d.ts +1 -1
  30. package/dist/esm/components/FieldSlider/FieldSlider.js +2 -2
  31. package/dist/esm/components/FieldSlider/styles.module.css +7 -0
  32. package/dist/esm/components/FieldStepper/FieldStepper.js +2 -2
  33. package/dist/esm/components/FieldStepper/styles.module.css +52 -6
  34. package/dist/esm/components/FieldTextArea/FieldTextArea.js +1 -1
  35. package/dist/esm/components/FieldTextArea/styles.module.css +36 -39
  36. package/dist/esm/components/FieldTime/FieldTime.js +1 -1
  37. package/dist/esm/components/shared/helperComponents/FieldElementButton/styles.module.css +22 -22
  38. package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.d.ts +1 -3
  39. package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.js +2 -2
  40. package/dist/esm/components/shared/styles.module.css +124 -151
  41. package/dist/esm/style.css +271 -245
  42. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  43. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  44. package/package.json +17 -17
  45. package/src/components/FieldColor/FieldColor.tsx +1 -1
  46. package/src/components/FieldColor/styles.module.scss +7 -5
  47. package/src/components/FieldCombo/FieldCombo.tsx +1 -1
  48. package/src/components/FieldDate/styles.module.scss +1 -1
  49. package/src/components/FieldSecure/FieldSecure.tsx +1 -2
  50. package/src/components/FieldSelect/FieldSelect.tsx +0 -2
  51. package/src/components/FieldSelect/styles.module.scss +5 -7
  52. package/src/components/FieldSelect/types.ts +0 -1
  53. package/src/components/FieldSlider/FieldSlider.tsx +3 -3
  54. package/src/components/FieldSlider/styles.module.scss +8 -0
  55. package/src/components/FieldStepper/FieldStepper.tsx +41 -46
  56. package/src/components/FieldStepper/styles.module.scss +60 -10
  57. package/src/components/FieldTextArea/FieldTextArea.tsx +1 -2
  58. package/src/components/FieldTextArea/styles.module.scss +22 -33
  59. package/src/components/FieldTime/FieldTime.tsx +1 -1
  60. package/src/components/shared/helperComponents/FieldElementButton/styles.module.scss +2 -2
  61. package/src/components/shared/helperComponents/FieldShell/FieldShell.tsx +0 -4
  62. package/src/components/shared/styles.module.scss +17 -58
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-ru/ds-fields",
3
- "version": "2.2.7-preview-d50a3da1.0",
3
+ "version": "2.2.7-preview-e30f4f90.0",
4
4
  "description": "Поля ввода с единой обёрткой label/caption/hint/error и общими осями size/validationState — текст, пароль, число, ползунок, дата, время, выбор, цвет.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,24 +39,24 @@
39
39
  "classnames": "^2.5.1",
40
40
  "merge-refs": "^2.0.0",
41
41
  "react-textarea-autosize": "^8.5.9",
42
- "@cloud-ru/ds-bottom-sheet": "1.1.3-preview-d50a3da1.0",
43
42
  "@cloud-ru/ds-adaptive": "1.0.1",
44
- "@cloud-ru/ds-color-picker": "1.0.8-preview-d50a3da1.0",
45
- "@cloud-ru/ds-dropdown": "1.0.10-preview-d50a3da1.0",
46
- "@cloud-ru/ds-button": "1.0.3-preview-d50a3da1.0",
47
- "@cloud-ru/ds-icons": "1.2.1-preview-d50a3da1.0",
43
+ "@cloud-ru/ds-button": "1.0.2",
44
+ "@cloud-ru/ds-calendar": "1.0.19-preview-e30f4f90.0",
48
45
  "@cloud-ru/ds-divider": "1.0.1",
49
- "@cloud-ru/ds-field-decorator": "1.0.7-preview-d50a3da1.0",
50
- "@cloud-ru/ds-list": "2.2.1-preview-d50a3da1.0",
51
- "@cloud-ru/ds-input-private": "1.0.6-preview-d50a3da1.0",
52
- "@cloud-ru/ds-materials": "1.0.2-preview-d50a3da1.0",
53
- "@cloud-ru/ds-loader": "1.0.2-preview-d50a3da1.0",
54
- "@cloud-ru/ds-scroll": "1.0.3-preview-d50a3da1.0",
55
- "@cloud-ru/ds-slider": "1.0.6-preview-d50a3da1.0",
56
- "@cloud-ru/ds-tag": "1.1.3-preview-d50a3da1.0",
46
+ "@cloud-ru/ds-bottom-sheet": "1.1.3-preview-e30f4f90.0",
47
+ "@cloud-ru/ds-color-picker": "1.0.8-preview-e30f4f90.0",
48
+ "@cloud-ru/ds-dropdown": "1.0.10-preview-e30f4f90.0",
49
+ "@cloud-ru/ds-field-decorator": "1.0.7-preview-e30f4f90.0",
50
+ "@cloud-ru/ds-icons": "1.2.0",
51
+ "@cloud-ru/ds-input-private": "1.0.5",
52
+ "@cloud-ru/ds-list": "2.2.1-preview-e30f4f90.0",
53
+ "@cloud-ru/ds-loader": "1.0.1",
54
+ "@cloud-ru/ds-scroll": "1.0.3-preview-e30f4f90.0",
57
55
  "@cloud-ru/ds-skeleton": "1.0.1",
58
- "@cloud-ru/ds-tooltip": "1.0.6-preview-d50a3da1.0",
59
- "@cloud-ru/ds-utils": "1.1.0",
60
- "@cloud-ru/ds-calendar": "1.0.19-preview-d50a3da1.0"
56
+ "@cloud-ru/ds-materials": "1.0.1",
57
+ "@cloud-ru/ds-tag": "1.1.3-preview-e30f4f90.0",
58
+ "@cloud-ru/ds-slider": "1.0.5",
59
+ "@cloud-ru/ds-tooltip": "1.0.5",
60
+ "@cloud-ru/ds-utils": "1.1.0"
61
61
  }
62
62
  }
@@ -329,8 +329,8 @@ export const FieldColor = forwardRef<HTMLInputElement, FieldColorProps>(function
329
329
  {...restInputProps}
330
330
  data-test-id={TEST_IDS.fieldColorInput}
331
331
  />
332
+ {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
332
333
  </div>
333
- {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
334
334
  </div>
335
335
  {/* Chevron присутствует во всех состояниях, включая readonly — паритет с Figma
336
336
  readonly-варианты тоже показывают chevron-down рядом с copy). */}
@@ -1,12 +1,14 @@
1
1
  @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
2
  @use '@cloud-ru/ds-figma-variables/build/scss/components/colorPicker.module' as colorPicker;
3
3
 
4
- // FieldColor size → размер previewColor в токенах colorPicker (Figma fieldSelectColor):
5
- // s → xs (16), m и l → s (24); у L-поля образец не растёт до 32.
4
+ // FieldColor size → colorPicker previewColor size (Figma fieldSelectColor):
5
+ // s → colorPicker s (16px / radius 4), m → colorPicker m (24px / radius 8),
6
+ // l → colorPicker m (24px / radius 8) — в макете L-поле референсит m-токен previewColor,
7
+ // swatch не увеличивается до 32.
6
8
  $swatch-sizes: (
7
- 's': 'xs',
8
- 'm': 's',
9
- 'l': 's',
9
+ 's': 's',
10
+ 'm': 'm',
11
+ 'l': 'm',
10
12
  );
11
13
 
12
14
  .trigger {
@@ -453,8 +453,8 @@ export const FieldCombo = forwardRef<HTMLInputElement, FieldComboProps>(function
453
453
  autoFocus={resolvedAutoFocus}
454
454
  data-test-id={innerTestIds?.input ?? TEST_IDS.fieldComboInput}
455
455
  />
456
+ {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
456
457
  </div>
457
- {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
458
458
  {postfix && <span className={fieldStyles.postfix}>{postfix}</span>}
459
459
  </div>
460
460
  {iconAfter && <div className={fieldStyles.iconSlot}>{iconAfter}</div>}
@@ -27,7 +27,7 @@ $sizes: 's', 'm', 'l';
27
27
 
28
28
  @each $size in $sizes {
29
29
  [data-size='#{$size}'] & {
30
- gap: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'inputWrapper', 'gap');
30
+ gap: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'inputLine', 'gap');
31
31
  }
32
32
  }
33
33
  }
@@ -346,8 +346,8 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(functi
346
346
  data-test-id={TEST_IDS.fieldSecureInput}
347
347
  />
348
348
  </WithSkeleton>
349
+ {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
349
350
  </div>
350
- {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
351
351
  </div>
352
352
  </div>
353
353
  </div>
@@ -358,7 +358,6 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(functi
358
358
  </div>
359
359
  <FieldElementButton
360
360
  ref={hideButtonRef}
361
- className={fieldStyles.elementButtonInheritDisabled}
362
361
  variant='after'
363
362
  size={size}
364
363
  action={hidden ? <EyeSVG /> : <EyeClosedSVG />}
@@ -92,7 +92,6 @@ export const FieldSelect = forwardRef<HTMLInputElement, FieldSelectProps>(functi
92
92
  enableFuzzySearch = true,
93
93
  selectedOptionFormatter,
94
94
  footer,
95
- footerDivider,
96
95
  footerActiveElementsRefs,
97
96
  loading,
98
97
  noDataState,
@@ -705,7 +704,6 @@ export const FieldSelect = forwardRef<HTMLInputElement, FieldSelectProps>(functi
705
704
  selection={droplistSelection}
706
705
  closeDroplistOnItemClick={closeDroplistOnItemClick}
707
706
  footer={footer}
708
- footerDivider={footerDivider}
709
707
  footerActiveElementsRefs={footerActiveElementsRefs}
710
708
  loading={loading}
711
709
  noDataState={noDataState}
@@ -54,16 +54,14 @@ $chip-sizes: (
54
54
 
55
55
  @each $size, $tagSize in $chip-sizes {
56
56
  [data-size='#{$size}'] & {
57
- $contentPaddingTop: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'padding-top');
58
- $contentPaddingBottom: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'padding-bottom');
59
- $inputWrapper: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'inputWrapper');
60
57
  // gap = tag.tagRow.gap по mapped tag size (Figma fieldSelectMultiple chips: 2 для xs/s) —
61
58
  // одновременно column- и row-gap при flex-wrap (отступ между чипами и между рядами).
62
59
  gap: base.simple-var(tag.$tag, 'anatomy', 'size', $tagSize, 'tagRow', 'gap');
63
-
64
- // Отступы рядов чипов по макету: вертикальные — contentWrapper.verticalPadding, горизонтальные — inputWrapper.
65
- padding: $contentPaddingTop base.simple-var($inputWrapper, 'padding-right') $contentPaddingBottom
66
- base.simple-var($inputWrapper, 'padding-left');
60
+ // Вертикальные отступы строк чипов (Figma fieldContainer.contentWrapper.verticalPadding) —
61
+ // shared-поле их не применяет (single-line центрируется), а мультиселекту они нужны, чтобы
62
+ // ряды чипов не липли к краям контейнера (пропавшие отступы по макету).
63
+ padding-top: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'padding-top');
64
+ padding-bottom: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'padding-bottom');
67
65
  }
68
66
  }
69
67
  }
@@ -23,7 +23,6 @@ type FieldSelectDecoratorProps = Omit<FieldDecoratorProps, 'children' | 'validat
23
23
  type DroplistPassthrough = Pick<
24
24
  DroplistProps,
25
25
  | 'footer'
26
- | 'footerDivider'
27
26
  | 'footerActiveElementsRefs'
28
27
  | 'loading'
29
28
  | 'noDataState'
@@ -1,7 +1,7 @@
1
1
  import { Button } from '@cloud-ru/ds-button';
2
2
  import { FieldDecorator, FieldDecoratorProps, SIZE, VALIDATION_STATE } from '@cloud-ru/ds-field-decorator';
3
3
  import { CheckSVG, CopySVG } from '@cloud-ru/ds-icons/interface/system';
4
- import { BUTTON_SIZE_MAP, INPUT_MODE, InputPrivate, TYPE } from '@cloud-ru/ds-input-private';
4
+ import { INPUT_MODE, InputPrivate, TYPE } from '@cloud-ru/ds-input-private';
5
5
  import { Slider, SliderProps } from '@cloud-ru/ds-slider';
6
6
  import { extractSupportProps, useValueControl } from '@cloud-ru/ds-utils';
7
7
  import cn from 'classnames';
@@ -438,13 +438,13 @@ export const FieldSlider = forwardRef<HTMLInputElement, FieldSliderProps>(functi
438
438
  />
439
439
  </div>
440
440
  {showCopyUi && (
441
- <span className={fieldStyles.postfixButtonsSlot}>
441
+ <span className={styles.copyButton}>
442
442
  <Button
443
443
  innerRef={copyButtonRef}
444
444
  type='button'
445
445
  view='function'
446
446
  appearance='neutral'
447
- size={BUTTON_SIZE_MAP[size]}
447
+ size={size}
448
448
  icon={copied ? <CheckSVG /> : <CopySVG />}
449
449
  onClick={handleCopy}
450
450
  onKeyDown={handleCopyButtonKeyDown}
@@ -37,3 +37,11 @@ $sizes: 's', 'm', 'l';
37
37
 
38
38
  color: base.$sn-theme-color-available-version-textTertiary;
39
39
  }
40
+
41
+ // Copy-кнопка (readonly) центрируется в строке значения.
42
+ .copyButton {
43
+ display: flex;
44
+ flex-shrink: 0;
45
+ align-items: center;
46
+ justify-content: center;
47
+ }
@@ -260,7 +260,7 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(func
260
260
 
261
261
  // Ширина инпута по числу символов значения (минимум 2ch), задаётся через `ch`-юниты в CSS-переменной —
262
262
  // без px-хардкода и подгонки под ширину глифа.
263
- const inputWidthCh = Math.max(1, String(value).length);
263
+ const inputWidthCh = Math.max(2, String(value).length);
264
264
 
265
265
  const handleMouseEnter = useCallback(() => {
266
266
  if (!readOnly) {
@@ -374,56 +374,51 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(func
374
374
  </div>
375
375
  <div className={fieldStyles.fieldContainer}>
376
376
  <div className={fieldStyles.contentWrapper}>
377
- <div className={cn(fieldStyles.inputLine, styles.inputLine)}>
378
- {prefix && <span className={fieldStyles.prefix}>{prefix}</span>}
379
- <div className={cn(fieldStyles.inputArea, styles.inputArea)}>
380
- <div
381
- className={styles.inputWrapper}
382
- style={{ '--field-stepper-input-ch': inputWidthCh } as CSSProperties}
383
- >
384
- <InputPrivate
385
- ref={mergeRefs(ref, localRef)}
386
- className={cn(fieldStyles.fieldInput, styles.input)}
387
- value={String(value)}
388
- type={TYPE.Number}
389
- disabled={disabled}
390
- readonly={readOnly}
391
- onChange={handleInputChange}
392
- onFocus={handleFocus}
393
- onBlur={handleBlur}
394
- onKeyDown={handleInputKeyDown}
395
- id={id}
396
- name={name}
397
- min={min}
398
- max={max}
399
- step={step}
400
- autoFocus={resolvedAutoFocus}
401
- data-test-id={TEST_IDS.fieldStepperInput}
402
- {...extractSupportProps(rest)}
403
- />
404
- </div>
377
+ <div className={styles.valueArea}>
378
+ {prefix && <span className={styles.affix}>{prefix}</span>}
379
+ <div className={styles.inputWrapper} style={{ '--field-stepper-input-ch': inputWidthCh } as CSSProperties}>
380
+ <InputPrivate
381
+ ref={mergeRefs(ref, localRef)}
382
+ className={cn(fieldStyles.fieldInput, styles.input)}
383
+ value={String(value)}
384
+ type={TYPE.Number}
385
+ disabled={disabled}
386
+ readonly={readOnly}
387
+ onChange={handleInputChange}
388
+ onFocus={handleFocus}
389
+ onBlur={handleBlur}
390
+ onKeyDown={handleInputKeyDown}
391
+ id={id}
392
+ name={name}
393
+ min={min}
394
+ max={max}
395
+ step={step}
396
+ autoFocus={resolvedAutoFocus}
397
+ data-test-id={TEST_IDS.fieldStepperInput}
398
+ {...extractSupportProps(rest)}
399
+ />
405
400
  </div>
406
- {postfix && <span className={fieldStyles.postfix}>{postfix}</span>}
407
- {showCopyUi && (
408
- <span className={fieldStyles.postfixButtonsSlot}>
409
- <Button
410
- innerRef={copyButtonRef}
411
- type='button'
412
- view='function'
413
- appearance='neutral'
414
- size={BUTTON_SIZE_MAP[size]}
415
- icon={copied ? <CheckSVG /> : <CopySVG />}
416
- onClick={handleCopy}
417
- onKeyDown={handleCopyButtonKeyDown}
418
- tabIndex={-1}
419
- data-test-id={TEST_IDS.fieldStepperCopyButton}
420
- />
421
- </span>
422
- )}
401
+ {postfix && <span className={styles.affix}>{postfix}</span>}
423
402
  </div>
424
403
  </div>
425
404
  </div>
426
405
  <div className={fieldStyles.elementWrapperAfter}>
406
+ {showCopyUi && (
407
+ <span className={styles.copyButton}>
408
+ <Button
409
+ innerRef={copyButtonRef}
410
+ type='button'
411
+ view='function'
412
+ appearance='neutral'
413
+ size={BUTTON_SIZE_MAP[size]}
414
+ icon={copied ? <CheckSVG /> : <CopySVG />}
415
+ onClick={handleCopy}
416
+ onKeyDown={handleCopyButtonKeyDown}
417
+ tabIndex={-1}
418
+ data-test-id={TEST_IDS.fieldStepperCopyButton}
419
+ />
420
+ </span>
421
+ )}
427
422
  <div className={fieldStyles.lineWrapper} data-outline={background || undefined}>
428
423
  <Divider orientation='vertical' variant='regular' />
429
424
  </div>
@@ -1,4 +1,15 @@
1
1
  @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/field.module' as field;
3
+
4
+ $sizes: 's', 'm', 'l';
5
+
6
+ // Figma-typo-мост: stepper inputLine.gap для size=m ссылается на токен size=s (=2px),
7
+ // а не на size=m (=4px). s→s(2), m→s(2), l→l(6).
8
+ $inputLineGapSizes: (
9
+ 's': 's',
10
+ 'm': 's',
11
+ 'l': 'l',
12
+ );
2
13
 
3
14
  // Tooltip оборачивает −/+ в block-триггер: растягиваем его на всю высоту слота,
4
15
  // чтобы buttonField-кнопка внутри занимала полную высоту поля (как в FieldText).
@@ -8,30 +19,56 @@
8
19
  height: 100%;
9
20
  }
10
21
 
11
- // В макете префикс, значение и постфикс — группа по центру поля, обёртка значения по содержимому.
12
- .inputLine {
13
- justify-content: center;
22
+ // Copy-кнопка (readonly) центрируется во всю высоту слота, как и step-триггеры.
23
+ .copyButton {
24
+ display: flex;
25
+ align-items: center;
26
+ height: 100%;
14
27
  }
15
28
 
16
- .inputArea {
17
- flex: 0 1 auto;
29
+ .valueArea {
30
+ display: flex;
31
+ flex: 1 1 auto;
32
+ align-items: baseline;
33
+ justify-content: center;
34
+
35
+ min-width: 0;
36
+
37
+ color: base.$sn-theme-color-available-version-textMain;
38
+ text-align: center;
39
+
40
+ @each $size, $tokenSize in $inputLineGapSizes {
41
+ [data-size='#{$size}'] & {
42
+ gap: base.simple-var(
43
+ field.$field,
44
+ 'anatomy',
45
+ 'size',
46
+ $tokenSize,
47
+ 'fieldContainer',
48
+ 'contentWrapper',
49
+ 'inputLine',
50
+ 'gap'
51
+ );
52
+ }
53
+ }
18
54
  }
19
55
 
20
56
  .inputWrapper {
21
57
  display: flex;
22
58
  flex: 0 0 auto;
59
+
60
+ // ширина по числу символов значения (var выставляется компонентом), минимум 2ch.
61
+ // +1ch — запас под каретку и субпиксельное округление: без него значение шириной ровно
62
+ // N символов (напр. «100» = 3ch) обрезается ellipsis'ом инпута до «1…».
63
+ width: calc((var(--field-stepper-input-ch, 2) + 1) * 1ch);
23
64
  max-width: 100%;
24
65
  }
25
66
 
26
67
  .input {
27
68
  flex: 0 0 auto;
28
69
 
29
- // Ширина по числу символов значения (var выставляется компонентом). ch считается от шрифта
30
- // самого input (типографика по размеру поля); цифры SB Sans моноширинные — 1 символ = 1ch.
31
- // Запас dimension-2 — под каретку и округление, иначе «100» обрезается до «1…».
32
- width: calc(var(--field-stepper-input-ch, 1) * 1ch + #{base.$sn-primitive-dimension-2});
70
+ width: 100%;
33
71
  min-width: 1ch;
34
- max-width: 100%;
35
72
 
36
73
  text-align: center;
37
74
 
@@ -46,3 +83,16 @@
46
83
  -webkit-appearance: none;
47
84
  }
48
85
  }
86
+
87
+ .affix {
88
+ flex-shrink: 0;
89
+ color: base.$sn-theme-color-available-version-textTertiary;
90
+
91
+ // DS-типографика по size из корневого fieldWrapper. Без этого фолбэк на Nunito Sans 16/24
92
+ // визуально не сходится с инпутом.
93
+ @each $size in $sizes {
94
+ [data-size='#{$size}'] & {
95
+ @include base.composite-var(base.$sn-regular, 'body', $size);
96
+ }
97
+ }
98
+ }
@@ -314,7 +314,6 @@ export const FieldTextArea = forwardRef<HTMLTextAreaElement, FieldTextAreaProps>
314
314
  hover={hover}
315
315
  header={header}
316
316
  footer={footer}
317
- resizable={isResizable}
318
317
  style={{ '--field-textarea-min-rows': minRows, '--field-textarea-max-rows': maxRows } as CSSProperties}
319
318
  onMouseEnter={handleMouseEnter}
320
319
  onMouseLeave={handleMouseLeave}
@@ -336,7 +335,7 @@ export const FieldTextArea = forwardRef<HTMLTextAreaElement, FieldTextAreaProps>
336
335
  overflow={{ x: 'hidden' }}
337
336
  data-test-id={TEST_IDS.fieldTextAreaScrollArea}
338
337
  >
339
- <div className={fieldStyles.inputArea}>
338
+ <div className={fieldStyles.inputLine}>
340
339
  <TextareaAutosize
341
340
  ref={mergeRefs(ref, localRef)}
342
341
  className={styles.textarea}
@@ -20,13 +20,6 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
20
20
  @each $size in $sizes {
21
21
  [data-size='#{$size}'] & {
22
22
  --field-textarea-row-height: #{base.simple-var(base.$sn-regular, 'body', $size, 'line-height')};
23
-
24
- // Полоса скролла в мастере прижата к правому краю поля и накрывает его боковой паддинг
25
- // (`resizeWrapper` — absolute, anchor right). Скролл рисуется по краю контейнера-хоста,
26
- // поэтому хост выводим за паддинг отрицательным отступом.
27
- margin-right: calc(
28
- -1 * #{base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'padding-right')}
29
- );
30
23
  }
31
24
  }
32
25
  }
@@ -71,12 +64,13 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
71
64
  color: base.$sn-theme-color-available-version-textTertiary;
72
65
  }
73
66
 
74
- // DS-типографика per-size (без неё textarea наследует шрифт Storybook root).
75
- // Минимальную высоту держит контейнер оболочки: одна строка, у resizable — multiLine-токен.
67
+ // DS-типографика + min-height multiLine из field-токенов per-size.
68
+ // Без этого textarea наследует Nunito Sans 16/24 от Storybook root и не имеет минимальной высоты.
76
69
  @each $size in $sizes {
77
70
  [data-size='#{$size}'] & {
78
- // Резерв справа под колонку clear/copy-кнопок: в мастере текст упирается в неё, а полоса
79
- // скролла идёт поверх. Кнопки позиционируются абсолютно (см. `.postfixColumn`).
71
+ // Резерв справа под колонку clear/copy-кнопок (квадрат centeredWrapper) + скроллбар
72
+ // + горизонтальный паддинг inputLine (отступ слота кнопок от скроллбара по Figma):
73
+ // кнопки позиционируются абсолютно поверх скролл-области (см. .postfixColumn).
80
74
  $button-slot: base.simple-var(
81
75
  field.$field,
82
76
  'anatomy',
@@ -84,13 +78,21 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
84
78
  $size,
85
79
  'fieldContainer',
86
80
  'centeredWrapper',
87
- 'width'
81
+ 'max-height'
82
+ );
83
+ $input-line-padding: base.simple-var(
84
+ field.$field,
85
+ 'anatomy',
86
+ 'size',
87
+ $size,
88
+ 'fieldContainer',
89
+ 'contentWrapper',
90
+ 'inputLine',
91
+ 'padding-right'
88
92
  );
89
- // Хост скролла вынесен за правый паддинг поля (см. `.scrollContainer`), поэтому резерв
90
- // текста добирает его обратно. Паддинг строки уже даёт `.inputArea`, второй раз не считаем.
91
- $container-padding: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'padding-right');
92
93
 
93
- padding-right: calc(#{$button-slot} + #{$container-padding});
94
+ min-height: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'multiLine', 'min-height');
95
+ padding-right: calc(#{$button-slot} + #{$scrollbar-width} + #{$input-line-padding});
94
96
 
95
97
  @include base.composite-var(base.$sn-regular, 'body', $size);
96
98
  }
@@ -130,7 +132,7 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
130
132
  }
131
133
 
132
134
  // Кнопки clear/copy — поверх скролл-области, левее скроллбара (паритет с Figma и легаси:
133
- // порядок справа налево — край поля, скроллбар, inputWrapper-паддинг, кнопки). Место под них
135
+ // порядок справа налево — край поля, скроллбар, inputLine-паддинг, кнопки). Место под них
134
136
  // резервирует padding-right textarea выше. Grid-ячейки клампят слот каждой кнопки до
135
137
  // centeredWrapper-квадрата: сама кнопка крупнее слота и симметрично выступает за него
136
138
  // (тот же приём, что .postfixButtonsSlot в shared/styles для FieldText), первый слот
@@ -155,7 +157,7 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
155
157
  $size,
156
158
  'fieldContainer',
157
159
  'centeredWrapper',
158
- 'height'
160
+ 'max-height'
159
161
  );
160
162
  $input-line-padding: base.simple-var(
161
163
  field.$field,
@@ -164,7 +166,7 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
164
166
  $size,
165
167
  'fieldContainer',
166
168
  'contentWrapper',
167
- 'inputWrapper',
169
+ 'inputLine',
168
170
  'padding-right'
169
171
  );
170
172
  $line-height: base.simple-var(base.$sn-regular, 'body', $size, 'line-height');
@@ -181,21 +183,8 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
181
183
  // Кнопка центрируется по первой строке текста: verticalPadding (отступ контента сверху)
182
184
  // + центровка слота в line-height. Так 32px-кнопка верхним краем садится ровно на
183
185
  // внутреннюю границу поля (Figma), а не выступает над ней.
184
- $slot-width: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'centeredWrapper', 'width');
185
- $button-size: 'm';
186
-
187
- @if $size == 's' {
188
- $button-size: 's';
189
- }
190
-
191
- // Ширина кнопки — слот centeredWrapper из макета (16/24/24), высота остаётся высотой кнопки.
192
- --sn-button-anatomy-size-#{$button-size}-container-iconOnly-minWidth: #{$slot-width};
193
-
194
186
  top: calc(#{$vertical-padding} + (#{$line-height} - #{$square}) / 2);
195
-
196
- // Правый край кнопки совпадает с внутренней границей поля (Figma): колонку уже кнопки
197
- // на половину её выступа, поэтому компенсируем его.
198
- right: calc((#{$slot-width} - #{$square}) / 2);
187
+ right: calc(#{$scrollbar-width} + #{$input-line-padding});
199
188
  grid-auto-rows: $square;
200
189
  width: $square;
201
190
  }
@@ -345,8 +345,8 @@ export const FieldTime = forwardRef<HTMLInputElement, FieldTimeProps>(function F
345
345
  {...extractSupportProps(rest)}
346
346
  data-test-id={TEST_IDS.fieldTimeInput}
347
347
  />
348
+ {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
348
349
  </div>
349
- {postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
350
350
  </div>
351
351
  {/* Иконка часов — декоративная; дроплист открывается кликом по полю. */}
352
352
  <div className={fieldStyles.iconSlot} data-test-id={TEST_IDS.fieldTimeIcon} aria-hidden>
@@ -92,10 +92,10 @@ $sizes: 's', 'm', 'l';
92
92
  // s → icon-xs (16), m/l → icon-s (24). max-* (как в @cloud-ru/ds-button): @cloud-ru/ds-icons задают инлайн
93
93
  // `style="width:24"`, который перебивает width, но не max-width — поэтому клампится без JS-пропа.
94
94
  .elementWrapper svg {
95
- $iconSize: base.$sn-density-icon-s;
95
+ $iconSize: base.$sn-density-size-icon-s;
96
96
 
97
97
  @if $size == 's' {
98
- $iconSize: base.$sn-density-icon-xs;
98
+ $iconSize: base.$sn-density-size-icon-xs;
99
99
  }
100
100
 
101
101
  max-width: $iconSize;
@@ -31,8 +31,6 @@ export type FieldShellProps = WithSupportProps<{
31
31
  header?: ReactNode;
32
32
  /** Ряд элементов под контентом (действия, счётчик) */
33
33
  footer?: ReactNode;
34
- /** Многострочное поле с ручным изменением высоты — поднимает минимальную высоту контейнера */
35
- resizable?: boolean;
36
34
  /** CSS-класс оболочки (fieldWrapper) */
37
35
  className?: string;
38
36
  /** Inline-стиль оболочки (например, CSS-переменные высоты) */
@@ -60,7 +58,6 @@ export function FieldShell({
60
58
  hover,
61
59
  header,
62
60
  footer,
63
- resizable,
64
61
  className,
65
62
  style,
66
63
  onMouseEnter,
@@ -78,7 +75,6 @@ export function FieldShell({
78
75
  data-withbackground={background || undefined}
79
76
  data-focusvisible={focusVisible || undefined}
80
77
  data-hover={!readOnly && hover ? true : undefined}
81
- data-resizable={resizable || undefined}
82
78
  style={style}
83
79
  onMouseEnter={onMouseEnter}
84
80
  onMouseLeave={onMouseLeave}