@cloud-ru/ds-fields 2.2.7-preview-d90ac2be.0 → 2.3.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.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/dist/cjs/components/FieldColor/FieldColor.js +1 -1
- package/dist/cjs/components/FieldColor/styles.module.css +9 -9
- package/dist/cjs/components/FieldCombo/FieldCombo.js +1 -1
- package/dist/cjs/components/FieldDate/styles.module.css +3 -3
- package/dist/cjs/components/FieldSecure/FieldSecure.js +1 -1
- package/dist/cjs/components/FieldSelect/FieldSelect.js +2 -2
- package/dist/cjs/components/FieldSelect/styles.module.css +15 -18
- package/dist/cjs/components/FieldSelect/types.d.ts +1 -1
- package/dist/cjs/components/FieldSlider/FieldSlider.js +1 -1
- package/dist/cjs/components/FieldSlider/styles.module.css +0 -7
- package/dist/cjs/components/FieldStepper/FieldStepper.js +2 -2
- package/dist/cjs/components/FieldStepper/styles.module.css +6 -52
- package/dist/cjs/components/FieldTextArea/FieldTextArea.js +1 -1
- package/dist/cjs/components/FieldTextArea/styles.module.css +39 -36
- package/dist/cjs/components/FieldTime/FieldTime.js +1 -1
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/styles.module.css +22 -22
- package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.d.ts +3 -1
- package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.js +2 -2
- package/dist/cjs/components/shared/styles.module.css +151 -124
- package/dist/cjs/style.css +245 -271
- package/dist/esm/components/FieldColor/FieldColor.js +1 -1
- package/dist/esm/components/FieldColor/styles.module.css +9 -9
- package/dist/esm/components/FieldCombo/FieldCombo.js +1 -1
- package/dist/esm/components/FieldDate/styles.module.css +3 -3
- package/dist/esm/components/FieldSecure/FieldSecure.js +1 -1
- package/dist/esm/components/FieldSelect/FieldSelect.js +2 -2
- package/dist/esm/components/FieldSelect/styles.module.css +15 -18
- package/dist/esm/components/FieldSelect/types.d.ts +1 -1
- package/dist/esm/components/FieldSlider/FieldSlider.js +2 -2
- package/dist/esm/components/FieldSlider/styles.module.css +0 -7
- package/dist/esm/components/FieldStepper/FieldStepper.js +2 -2
- package/dist/esm/components/FieldStepper/styles.module.css +6 -52
- package/dist/esm/components/FieldTextArea/FieldTextArea.js +1 -1
- package/dist/esm/components/FieldTextArea/styles.module.css +39 -36
- package/dist/esm/components/FieldTime/FieldTime.js +1 -1
- package/dist/esm/components/shared/helperComponents/FieldElementButton/styles.module.css +22 -22
- package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.d.ts +3 -1
- package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.js +2 -2
- package/dist/esm/components/shared/styles.module.css +151 -124
- package/dist/esm/style.css +245 -271
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +20 -20
- package/src/components/FieldColor/FieldColor.tsx +1 -1
- package/src/components/FieldColor/styles.module.scss +5 -7
- package/src/components/FieldCombo/FieldCombo.tsx +1 -1
- package/src/components/FieldDate/styles.module.scss +1 -1
- package/src/components/FieldSecure/FieldSecure.tsx +2 -1
- package/src/components/FieldSelect/FieldSelect.tsx +2 -0
- package/src/components/FieldSelect/styles.module.scss +7 -5
- package/src/components/FieldSelect/types.ts +1 -0
- package/src/components/FieldSlider/FieldSlider.tsx +3 -3
- package/src/components/FieldSlider/styles.module.scss +0 -8
- package/src/components/FieldStepper/FieldStepper.tsx +46 -41
- package/src/components/FieldStepper/styles.module.scss +10 -60
- package/src/components/FieldTextArea/FieldTextArea.tsx +2 -1
- package/src/components/FieldTextArea/styles.module.scss +33 -22
- package/src/components/FieldTime/FieldTime.tsx +1 -1
- package/src/components/shared/helperComponents/FieldElementButton/styles.module.scss +2 -2
- package/src/components/shared/helperComponents/FieldShell/FieldShell.tsx +4 -0
- package/src/components/shared/styles.module.scss +58 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloud-ru/ds-fields",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.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-adaptive": "1.0.1",
|
|
43
|
-
"@cloud-ru/ds-
|
|
44
|
-
"@cloud-ru/ds-
|
|
45
|
-
"@cloud-ru/ds-
|
|
46
|
-
"@cloud-ru/ds-
|
|
47
|
-
"@cloud-ru/ds-
|
|
48
|
-
"@cloud-ru/ds-
|
|
49
|
-
"@cloud-ru/ds-
|
|
50
|
-
"@cloud-ru/ds-
|
|
51
|
-
"@cloud-ru/ds-
|
|
52
|
-
"@cloud-ru/ds-
|
|
53
|
-
"@cloud-ru/ds-
|
|
54
|
-
"@cloud-ru/ds-
|
|
55
|
-
"@cloud-ru/ds-
|
|
56
|
-
"@cloud-ru/ds-
|
|
57
|
-
"@cloud-ru/ds-
|
|
58
|
-
"@cloud-ru/ds-
|
|
59
|
-
"@cloud-ru/ds-
|
|
60
|
-
"@cloud-ru/ds-
|
|
42
|
+
"@cloud-ru/ds-adaptive": "^1.0.1",
|
|
43
|
+
"@cloud-ru/ds-button": "^1.0.3",
|
|
44
|
+
"@cloud-ru/ds-color-picker": "^1.0.8",
|
|
45
|
+
"@cloud-ru/ds-divider": "^1.0.1",
|
|
46
|
+
"@cloud-ru/ds-dropdown": "^1.0.10",
|
|
47
|
+
"@cloud-ru/ds-field-decorator": "^1.0.7",
|
|
48
|
+
"@cloud-ru/ds-icons": "^1.2.1",
|
|
49
|
+
"@cloud-ru/ds-input-private": "^1.0.6",
|
|
50
|
+
"@cloud-ru/ds-list": "^2.2.1",
|
|
51
|
+
"@cloud-ru/ds-loader": "^1.0.2",
|
|
52
|
+
"@cloud-ru/ds-materials": "^2.0.0",
|
|
53
|
+
"@cloud-ru/ds-scroll": "^1.0.3",
|
|
54
|
+
"@cloud-ru/ds-skeleton": "^1.0.1",
|
|
55
|
+
"@cloud-ru/ds-slider": "^1.0.6",
|
|
56
|
+
"@cloud-ru/ds-tag": "^1.1.3",
|
|
57
|
+
"@cloud-ru/ds-utils": "^1.1.0",
|
|
58
|
+
"@cloud-ru/ds-tooltip": "^1.0.6",
|
|
59
|
+
"@cloud-ru/ds-calendar": "^1.0.19",
|
|
60
|
+
"@cloud-ru/ds-bottom-sheet": "^1.1.3"
|
|
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>}
|
|
333
332
|
</div>
|
|
333
|
+
{postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
|
|
334
334
|
</div>
|
|
335
335
|
{/* Chevron присутствует во всех состояниях, включая readonly — паритет с Figma
|
|
336
336
|
readonly-варианты тоже показывают chevron-down рядом с copy). */}
|
|
@@ -1,14 +1,12 @@
|
|
|
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 →
|
|
5
|
-
// s →
|
|
6
|
-
// l → colorPicker m (24px / radius 8) — в макете L-поле референсит m-токен previewColor,
|
|
7
|
-
// swatch не увеличивается до 32.
|
|
4
|
+
// FieldColor size → размер previewColor в токенах colorPicker (Figma fieldSelectColor):
|
|
5
|
+
// s → xs (16), m и l → s (24); у L-поля образец не растёт до 32.
|
|
8
6
|
$swatch-sizes: (
|
|
9
|
-
's': '
|
|
10
|
-
'm': '
|
|
11
|
-
'l': '
|
|
7
|
+
's': 'xs',
|
|
8
|
+
'm': 's',
|
|
9
|
+
'l': 's',
|
|
12
10
|
);
|
|
13
11
|
|
|
14
12
|
.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>}
|
|
457
456
|
</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', '
|
|
30
|
+
gap: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'inputWrapper', '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>}
|
|
350
349
|
</div>
|
|
350
|
+
{postfixButtons && <span className={fieldStyles.postfixButtonsSlot}>{postfixButtons}</span>}
|
|
351
351
|
</div>
|
|
352
352
|
</div>
|
|
353
353
|
</div>
|
|
@@ -358,6 +358,7 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(functi
|
|
|
358
358
|
</div>
|
|
359
359
|
<FieldElementButton
|
|
360
360
|
ref={hideButtonRef}
|
|
361
|
+
className={fieldStyles.elementButtonInheritDisabled}
|
|
361
362
|
variant='after'
|
|
362
363
|
size={size}
|
|
363
364
|
action={hidden ? <EyeSVG /> : <EyeClosedSVG />}
|
|
@@ -92,6 +92,7 @@ export const FieldSelect = forwardRef<HTMLInputElement, FieldSelectProps>(functi
|
|
|
92
92
|
enableFuzzySearch = true,
|
|
93
93
|
selectedOptionFormatter,
|
|
94
94
|
footer,
|
|
95
|
+
footerDivider,
|
|
95
96
|
footerActiveElementsRefs,
|
|
96
97
|
loading,
|
|
97
98
|
noDataState,
|
|
@@ -704,6 +705,7 @@ export const FieldSelect = forwardRef<HTMLInputElement, FieldSelectProps>(functi
|
|
|
704
705
|
selection={droplistSelection}
|
|
705
706
|
closeDroplistOnItemClick={closeDroplistOnItemClick}
|
|
706
707
|
footer={footer}
|
|
708
|
+
footerDivider={footerDivider}
|
|
707
709
|
footerActiveElementsRefs={footerActiveElementsRefs}
|
|
708
710
|
loading={loading}
|
|
709
711
|
noDataState={noDataState}
|
|
@@ -54,14 +54,16 @@ $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');
|
|
57
60
|
// gap = tag.tagRow.gap по mapped tag size (Figma fieldSelectMultiple chips: 2 для xs/s) —
|
|
58
61
|
// одновременно column- и row-gap при flex-wrap (отступ между чипами и между рядами).
|
|
59
62
|
gap: base.simple-var(tag.$tag, 'anatomy', 'size', $tagSize, 'tagRow', 'gap');
|
|
60
|
-
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
padding-bottom: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'contentWrapper', 'padding-bottom');
|
|
63
|
+
|
|
64
|
+
// Отступы рядов чипов по макету: вертикальные — contentWrapper.verticalPadding, горизонтальные — inputWrapper.
|
|
65
|
+
padding: $contentPaddingTop base.simple-var($inputWrapper, 'padding-right') $contentPaddingBottom
|
|
66
|
+
base.simple-var($inputWrapper, 'padding-left');
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
}
|
|
@@ -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 { INPUT_MODE, InputPrivate, TYPE } from '@cloud-ru/ds-input-private';
|
|
4
|
+
import { BUTTON_SIZE_MAP, 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={
|
|
441
|
+
<span className={fieldStyles.postfixButtonsSlot}>
|
|
442
442
|
<Button
|
|
443
443
|
innerRef={copyButtonRef}
|
|
444
444
|
type='button'
|
|
445
445
|
view='function'
|
|
446
446
|
appearance='neutral'
|
|
447
|
-
size={size}
|
|
447
|
+
size={BUTTON_SIZE_MAP[size]}
|
|
448
448
|
icon={copied ? <CheckSVG /> : <CopySVG />}
|
|
449
449
|
onClick={handleCopy}
|
|
450
450
|
onKeyDown={handleCopyButtonKeyDown}
|
|
@@ -37,11 +37,3 @@ $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(
|
|
263
|
+
const inputWidthCh = Math.max(1, String(value).length);
|
|
264
264
|
|
|
265
265
|
const handleMouseEnter = useCallback(() => {
|
|
266
266
|
if (!readOnly) {
|
|
@@ -374,51 +374,56 @@ 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={styles.
|
|
378
|
-
{prefix && <span className={
|
|
379
|
-
<div className={styles.
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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>
|
|
400
405
|
</div>
|
|
401
|
-
{postfix && <span className={
|
|
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
|
+
)}
|
|
402
423
|
</div>
|
|
403
424
|
</div>
|
|
404
425
|
</div>
|
|
405
426
|
<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
|
-
)}
|
|
422
427
|
<div className={fieldStyles.lineWrapper} data-outline={background || undefined}>
|
|
423
428
|
<Divider orientation='vertical' variant='regular' />
|
|
424
429
|
</div>
|
|
@@ -1,15 +1,4 @@
|
|
|
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
|
-
);
|
|
13
2
|
|
|
14
3
|
// Tooltip оборачивает −/+ в block-триггер: растягиваем его на всю высоту слота,
|
|
15
4
|
// чтобы buttonField-кнопка внутри занимала полную высоту поля (как в FieldText).
|
|
@@ -19,56 +8,30 @@ $inputLineGapSizes: (
|
|
|
19
8
|
height: 100%;
|
|
20
9
|
}
|
|
21
10
|
|
|
22
|
-
//
|
|
23
|
-
.
|
|
24
|
-
display: flex;
|
|
25
|
-
align-items: center;
|
|
26
|
-
height: 100%;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.valueArea {
|
|
30
|
-
display: flex;
|
|
31
|
-
flex: 1 1 auto;
|
|
32
|
-
align-items: baseline;
|
|
11
|
+
// В макете префикс, значение и постфикс — группа по центру поля, обёртка значения по содержимому.
|
|
12
|
+
.inputLine {
|
|
33
13
|
justify-content: center;
|
|
14
|
+
}
|
|
34
15
|
|
|
35
|
-
|
|
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
|
-
}
|
|
16
|
+
.inputArea {
|
|
17
|
+
flex: 0 1 auto;
|
|
54
18
|
}
|
|
55
19
|
|
|
56
20
|
.inputWrapper {
|
|
57
21
|
display: flex;
|
|
58
22
|
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);
|
|
64
23
|
max-width: 100%;
|
|
65
24
|
}
|
|
66
25
|
|
|
67
26
|
.input {
|
|
68
27
|
flex: 0 0 auto;
|
|
69
28
|
|
|
70
|
-
|
|
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});
|
|
71
33
|
min-width: 1ch;
|
|
34
|
+
max-width: 100%;
|
|
72
35
|
|
|
73
36
|
text-align: center;
|
|
74
37
|
|
|
@@ -83,16 +46,3 @@ $inputLineGapSizes: (
|
|
|
83
46
|
-webkit-appearance: none;
|
|
84
47
|
}
|
|
85
48
|
}
|
|
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,6 +314,7 @@ export const FieldTextArea = forwardRef<HTMLTextAreaElement, FieldTextAreaProps>
|
|
|
314
314
|
hover={hover}
|
|
315
315
|
header={header}
|
|
316
316
|
footer={footer}
|
|
317
|
+
resizable={isResizable}
|
|
317
318
|
style={{ '--field-textarea-min-rows': minRows, '--field-textarea-max-rows': maxRows } as CSSProperties}
|
|
318
319
|
onMouseEnter={handleMouseEnter}
|
|
319
320
|
onMouseLeave={handleMouseLeave}
|
|
@@ -335,7 +336,7 @@ export const FieldTextArea = forwardRef<HTMLTextAreaElement, FieldTextAreaProps>
|
|
|
335
336
|
overflow={{ x: 'hidden' }}
|
|
336
337
|
data-test-id={TEST_IDS.fieldTextAreaScrollArea}
|
|
337
338
|
>
|
|
338
|
-
<div className={fieldStyles.
|
|
339
|
+
<div className={fieldStyles.inputArea}>
|
|
339
340
|
<TextareaAutosize
|
|
340
341
|
ref={mergeRefs(ref, localRef)}
|
|
341
342
|
className={styles.textarea}
|
|
@@ -20,6 +20,13 @@ $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
|
+
);
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
}
|
|
@@ -64,13 +71,12 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
|
|
|
64
71
|
color: base.$sn-theme-color-available-version-textTertiary;
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
// DS-типографика
|
|
68
|
-
//
|
|
74
|
+
// DS-типографика per-size (без неё textarea наследует шрифт Storybook root).
|
|
75
|
+
// Минимальную высоту держит контейнер оболочки: одна строка, у resizable — multiLine-токен.
|
|
69
76
|
@each $size in $sizes {
|
|
70
77
|
[data-size='#{$size}'] & {
|
|
71
|
-
// Резерв справа под колонку clear/copy
|
|
72
|
-
//
|
|
73
|
-
// кнопки позиционируются абсолютно поверх скролл-области (см. .postfixColumn).
|
|
78
|
+
// Резерв справа под колонку clear/copy-кнопок: в мастере текст упирается в неё, а полоса
|
|
79
|
+
// скролла идёт поверх. Кнопки позиционируются абсолютно (см. `.postfixColumn`).
|
|
74
80
|
$button-slot: base.simple-var(
|
|
75
81
|
field.$field,
|
|
76
82
|
'anatomy',
|
|
@@ -78,21 +84,13 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
|
|
|
78
84
|
$size,
|
|
79
85
|
'fieldContainer',
|
|
80
86
|
'centeredWrapper',
|
|
81
|
-
'
|
|
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'
|
|
87
|
+
'width'
|
|
92
88
|
);
|
|
89
|
+
// Хост скролла вынесен за правый паддинг поля (см. `.scrollContainer`), поэтому резерв
|
|
90
|
+
// текста добирает его обратно. Паддинг строки уже даёт `.inputArea`, второй раз не считаем.
|
|
91
|
+
$container-padding: base.simple-var(field.$field, 'anatomy', 'size', $size, 'fieldContainer', 'padding-right');
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
padding-right: calc(#{$button-slot} + #{$scrollbar-width} + #{$input-line-padding});
|
|
93
|
+
padding-right: calc(#{$button-slot} + #{$container-padding});
|
|
96
94
|
|
|
97
95
|
@include base.composite-var(base.$sn-regular, 'body', $size);
|
|
98
96
|
}
|
|
@@ -132,7 +130,7 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
|
|
|
132
130
|
}
|
|
133
131
|
|
|
134
132
|
// Кнопки clear/copy — поверх скролл-области, левее скроллбара (паритет с Figma и легаси:
|
|
135
|
-
// порядок справа налево — край поля, скроллбар,
|
|
133
|
+
// порядок справа налево — край поля, скроллбар, inputWrapper-паддинг, кнопки). Место под них
|
|
136
134
|
// резервирует padding-right textarea выше. Grid-ячейки клампят слот каждой кнопки до
|
|
137
135
|
// centeredWrapper-квадрата: сама кнопка крупнее слота и симметрично выступает за него
|
|
138
136
|
// (тот же приём, что .postfixButtonsSlot в shared/styles для FieldText), первый слот
|
|
@@ -157,7 +155,7 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
|
|
|
157
155
|
$size,
|
|
158
156
|
'fieldContainer',
|
|
159
157
|
'centeredWrapper',
|
|
160
|
-
'
|
|
158
|
+
'height'
|
|
161
159
|
);
|
|
162
160
|
$input-line-padding: base.simple-var(
|
|
163
161
|
field.$field,
|
|
@@ -166,7 +164,7 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
|
|
|
166
164
|
$size,
|
|
167
165
|
'fieldContainer',
|
|
168
166
|
'contentWrapper',
|
|
169
|
-
'
|
|
167
|
+
'inputWrapper',
|
|
170
168
|
'padding-right'
|
|
171
169
|
);
|
|
172
170
|
$line-height: base.simple-var(base.$sn-regular, 'body', $size, 'line-height');
|
|
@@ -183,8 +181,21 @@ $scrollbar-width: base.simple-var(scroll.$scroll, 'anatomy', 'container', 'size'
|
|
|
183
181
|
// Кнопка центрируется по первой строке текста: verticalPadding (отступ контента сверху)
|
|
184
182
|
// + центровка слота в line-height. Так 32px-кнопка верхним краем садится ровно на
|
|
185
183
|
// внутреннюю границу поля (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
|
+
|
|
186
194
|
top: calc(#{$vertical-padding} + (#{$line-height} - #{$square}) / 2);
|
|
187
|
-
|
|
195
|
+
|
|
196
|
+
// Правый край кнопки совпадает с внутренней границей поля (Figma): колонку уже кнопки
|
|
197
|
+
// на половину её выступа, поэтому компенсируем его.
|
|
198
|
+
right: calc((#{$slot-width} - #{$square}) / 2);
|
|
188
199
|
grid-auto-rows: $square;
|
|
189
200
|
width: $square;
|
|
190
201
|
}
|
|
@@ -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>}
|
|
349
348
|
</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-
|
|
95
|
+
$iconSize: base.$sn-density-icon-s;
|
|
96
96
|
|
|
97
97
|
@if $size == 's' {
|
|
98
|
-
$iconSize: base.$sn-density-
|
|
98
|
+
$iconSize: base.$sn-density-icon-xs;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
max-width: $iconSize;
|
|
@@ -31,6 +31,8 @@ export type FieldShellProps = WithSupportProps<{
|
|
|
31
31
|
header?: ReactNode;
|
|
32
32
|
/** Ряд элементов под контентом (действия, счётчик) */
|
|
33
33
|
footer?: ReactNode;
|
|
34
|
+
/** Многострочное поле с ручным изменением высоты — поднимает минимальную высоту контейнера */
|
|
35
|
+
resizable?: boolean;
|
|
34
36
|
/** CSS-класс оболочки (fieldWrapper) */
|
|
35
37
|
className?: string;
|
|
36
38
|
/** Inline-стиль оболочки (например, CSS-переменные высоты) */
|
|
@@ -58,6 +60,7 @@ export function FieldShell({
|
|
|
58
60
|
hover,
|
|
59
61
|
header,
|
|
60
62
|
footer,
|
|
63
|
+
resizable,
|
|
61
64
|
className,
|
|
62
65
|
style,
|
|
63
66
|
onMouseEnter,
|
|
@@ -75,6 +78,7 @@ export function FieldShell({
|
|
|
75
78
|
data-withbackground={background || undefined}
|
|
76
79
|
data-focusvisible={focusVisible || undefined}
|
|
77
80
|
data-hover={!readOnly && hover ? true : undefined}
|
|
81
|
+
data-resizable={resizable || undefined}
|
|
78
82
|
style={style}
|
|
79
83
|
onMouseEnter={onMouseEnter}
|
|
80
84
|
onMouseLeave={onMouseLeave}
|