@cloud-ru/uikit-product-mobile-fields 0.11.24

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 (65) hide show
  1. package/CHANGELOG.md +1300 -0
  2. package/LICENSE +201 -0
  3. package/README.md +8 -0
  4. package/package.json +66 -0
  5. package/src/components/AdaptiveField/AdaptiveField.tsx +88 -0
  6. package/src/components/AdaptiveField/index.ts +1 -0
  7. package/src/components/MobileFieldDate/MobileFieldDate.tsx +375 -0
  8. package/src/components/MobileFieldDate/constants.ts +33 -0
  9. package/src/components/MobileFieldDate/index.ts +2 -0
  10. package/src/components/MobileFieldDate/styles.module.scss +75 -0
  11. package/src/components/MobileFieldDate/types.ts +6 -0
  12. package/src/components/MobileFieldDate/utils.ts +49 -0
  13. package/src/components/MobileFieldSelect/MobileFieldSelect.tsx +18 -0
  14. package/src/components/MobileFieldSelect/MobileFieldSelectMultiple.tsx +331 -0
  15. package/src/components/MobileFieldSelect/MobileFieldSelectSingle.tsx +300 -0
  16. package/src/components/MobileFieldSelect/hooks.tsx +195 -0
  17. package/src/components/MobileFieldSelect/index.ts +13 -0
  18. package/src/components/MobileFieldSelect/legacy/components/Items/hooks.tsx +53 -0
  19. package/src/components/MobileFieldSelect/legacy/components/index.ts +1 -0
  20. package/src/components/MobileFieldSelect/legacy/hooks.ts +38 -0
  21. package/src/components/MobileFieldSelect/legacy/index.ts +3 -0
  22. package/src/components/MobileFieldSelect/legacy/utils.ts +176 -0
  23. package/src/components/MobileFieldSelect/styles.module.scss +176 -0
  24. package/src/components/MobileFieldSelect/types.ts +156 -0
  25. package/src/components/MobileFieldSelect/utils/extractFieldDecoratorProps.ts +35 -0
  26. package/src/components/MobileFieldSelect/utils/extractListProps.ts +30 -0
  27. package/src/components/MobileFieldSelect/utils/getArrowIcon.ts +15 -0
  28. package/src/components/MobileFieldSelect/utils/index.ts +6 -0
  29. package/src/components/MobileFieldSelect/utils/options.tsx +88 -0
  30. package/src/components/MobileFieldSelect/utils/typeGuards.ts +38 -0
  31. package/src/components/MobileFieldSelect/utils/updateItems.ts +121 -0
  32. package/src/components/index.ts +3 -0
  33. package/src/constants/allFields.ts +11 -0
  34. package/src/constants/dateFields.ts +127 -0
  35. package/src/constants/index.ts +2 -0
  36. package/src/helperComponents/ButtonCopyValue/ButtonCopyValue.tsx +79 -0
  37. package/src/helperComponents/ButtonCopyValue/helpers.tsx +19 -0
  38. package/src/helperComponents/ButtonCopyValue/index.ts +1 -0
  39. package/src/helperComponents/ButtonCopyValue/styles.module.scss +5 -0
  40. package/src/helperComponents/FieldContainerPrivate/FieldContainerPrivate.tsx +79 -0
  41. package/src/helperComponents/FieldContainerPrivate/index.ts +1 -0
  42. package/src/helperComponents/FieldContainerPrivate/styles.module.scss +131 -0
  43. package/src/helperComponents/ItemContent/ItemContent.tsx +37 -0
  44. package/src/helperComponents/ItemContent/index.ts +1 -0
  45. package/src/helperComponents/ItemContent/styles.module.scss +80 -0
  46. package/src/helperComponents/index.ts +3 -0
  47. package/src/hooks/dateHandlers/index.ts +3 -0
  48. package/src/hooks/dateHandlers/useDateField.ts +275 -0
  49. package/src/hooks/dateHandlers/useDateFieldHelpersForMode.ts +145 -0
  50. package/src/hooks/dateHandlers/useFocusHandlers.ts +46 -0
  51. package/src/hooks/dateHandlers/useHandlers.ts +15 -0
  52. package/src/hooks/index.ts +5 -0
  53. package/src/hooks/styles.module.scss +17 -0
  54. package/src/hooks/useCopyButton.tsx +47 -0
  55. package/src/hooks/usePostfix.tsx +21 -0
  56. package/src/hooks/usePrefix.tsx +21 -0
  57. package/src/hooks/useValueControl.ts +15 -0
  58. package/src/index.ts +3 -0
  59. package/src/styles.module.scss +55 -0
  60. package/src/types/allFields.ts +9 -0
  61. package/src/types/dateFields.ts +14 -0
  62. package/src/types/index.ts +2 -0
  63. package/src/utils/adaptiveField.tsx +19 -0
  64. package/src/utils/dateFields.ts +75 -0
  65. package/src/utils/getValidationState.ts +6 -0
@@ -0,0 +1,79 @@
1
+ import copyToClipboard from 'copy-to-clipboard';
2
+ import { forwardRef, KeyboardEventHandler, MouseEvent, MouseEventHandler, useEffect, useRef, useState } from 'react';
3
+
4
+ import { ButtonSize } from '@snack-uikit/input-private';
5
+
6
+ import { AsyncValueRequest } from '../../types';
7
+ import { getIcon } from './helpers';
8
+ import styles from './styles.module.scss';
9
+
10
+ type ButtonCopyValueProps = {
11
+ size: ButtonSize;
12
+ valueToCopy?: string;
13
+ onKeyDown?: KeyboardEventHandler<HTMLButtonElement>;
14
+ onClick?: MouseEventHandler<HTMLButtonElement>;
15
+ tabIndex?: number;
16
+ onValueRequest?(): AsyncValueRequest;
17
+ disabled?: boolean;
18
+ };
19
+
20
+ export const ButtonCopyValue = forwardRef<HTMLButtonElement, ButtonCopyValueProps>(
21
+ ({ size, valueToCopy, tabIndex = -1, onKeyDown, onClick, onValueRequest, disabled }, ref) => {
22
+ const [isChecked, setIsChecked] = useState(false);
23
+ const timerId = useRef<ReturnType<typeof setTimeout>>();
24
+
25
+ const closeTooltip = () => setIsChecked(false);
26
+
27
+ const handleCopy = (event: MouseEvent<HTMLButtonElement>, asyncValue?: string) => {
28
+ const value = asyncValue || valueToCopy;
29
+
30
+ value && copyToClipboard(value, { format: 'text/plain' });
31
+
32
+ setIsChecked(true);
33
+
34
+ clearTimeout(timerId.current);
35
+ timerId.current = setTimeout(closeTooltip, 2000);
36
+
37
+ onClick?.(event);
38
+ };
39
+
40
+ const handleClick: MouseEventHandler<HTMLButtonElement> = event => {
41
+ event.stopPropagation();
42
+
43
+ if (onValueRequest) {
44
+ onValueRequest().then(({ success, value }) => {
45
+ if (success) {
46
+ handleCopy(event, value);
47
+ }
48
+ });
49
+ } else {
50
+ handleCopy(event);
51
+ }
52
+ };
53
+
54
+ useEffect(
55
+ () => () => {
56
+ closeTooltip();
57
+ clearTimeout(timerId.current);
58
+ },
59
+ [],
60
+ );
61
+
62
+ return (
63
+ <button
64
+ className={styles.buttonCopyValue}
65
+ data-size={size}
66
+ data-disabled={disabled || undefined}
67
+ onClick={handleClick}
68
+ data-test-id='button-copy-value'
69
+ ref={ref}
70
+ onKeyDown={onKeyDown}
71
+ tabIndex={tabIndex}
72
+ type='button'
73
+ disabled={disabled}
74
+ >
75
+ {getIcon({ size, isChecked })}
76
+ </button>
77
+ );
78
+ },
79
+ );
@@ -0,0 +1,19 @@
1
+ import { CheckSVG, CopySVG } from '@cloud-ru/uikit-product-icons';
2
+ import { BUTTON_SIZE, ButtonSize } from '@snack-uikit/input-private';
3
+
4
+ type GetIconProps = {
5
+ size: ButtonSize;
6
+ isChecked: boolean;
7
+ };
8
+
9
+ export function getIcon({ size, isChecked }: GetIconProps) {
10
+ switch (size) {
11
+ case BUTTON_SIZE.S: {
12
+ return isChecked ? <CheckSVG size={16} /> : <CopySVG size={16} />;
13
+ }
14
+ case BUTTON_SIZE.M:
15
+ default: {
16
+ return isChecked ? <CheckSVG size={24} /> : <CopySVG size={24} />;
17
+ }
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ export * from './ButtonCopyValue';
@@ -0,0 +1,5 @@
1
+ @use "../../styles.module";
2
+
3
+ .buttonCopyValue {
4
+ @include styles.button-styles();
5
+ }
@@ -0,0 +1,79 @@
1
+ import cn from 'classnames';
2
+ import { CSSProperties, MouseEventHandler, ReactNode, RefObject } from 'react';
3
+
4
+ import { Size } from '@snack-uikit/input-private';
5
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
6
+
7
+ import { VALIDATION_STATE } from '../../constants';
8
+ import { ContainerVariant, ValidationState } from '../../types';
9
+ import styles from './styles.module.scss';
10
+
11
+ export type FieldContainerPrivateProps = WithSupportProps<{
12
+ className?: string;
13
+ children: ReactNode;
14
+ size: Size;
15
+ validationState: ValidationState;
16
+ variant: ContainerVariant;
17
+ disabled: boolean;
18
+ readonly: boolean;
19
+ focused?: boolean;
20
+ selectable?: boolean;
21
+ style?: CSSProperties;
22
+ prefix?: ReactNode;
23
+ postfix?: ReactNode;
24
+ inputRef: RefObject<HTMLElement>;
25
+ onClick?: MouseEventHandler;
26
+ }>;
27
+
28
+ export function FieldContainerPrivate({
29
+ className,
30
+ children,
31
+ size,
32
+ validationState,
33
+ variant,
34
+ disabled,
35
+ readonly,
36
+ focused,
37
+ selectable,
38
+ style,
39
+ prefix,
40
+ postfix,
41
+ onClick,
42
+ ...rest
43
+ }: FieldContainerPrivateProps) {
44
+ const handleContainerClick: MouseEventHandler<HTMLDivElement> = event => {
45
+ if (disabled) {
46
+ return;
47
+ }
48
+ onClick?.(event);
49
+ };
50
+
51
+ return (
52
+ <div
53
+ className={cn(className, styles.container)}
54
+ style={style}
55
+ data-size={size}
56
+ data-validation={disabled || readonly ? VALIDATION_STATE.Default : validationState}
57
+ data-variant={variant}
58
+ data-disabled={disabled || undefined}
59
+ data-readonly={readonly || undefined}
60
+ data-focused={focused || undefined}
61
+ data-selectable={selectable || undefined}
62
+ data-test-id='field-container-private'
63
+ onClick={handleContainerClick}
64
+ role='textbox'
65
+ tabIndex={-1}
66
+ {...extractSupportProps(rest)}
67
+ >
68
+ {prefix && (
69
+ <span className={styles.prefix} data-test-id='field-container-private__prefix-icon'>
70
+ {prefix}
71
+ </span>
72
+ )}
73
+
74
+ {children}
75
+
76
+ {postfix && <span className={styles.postfix}>{postfix}</span>}
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1 @@
1
+ export * from './FieldContainerPrivate';
@@ -0,0 +1,131 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-fields';
2
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-element';
3
+
4
+ $sizes: 's', 'm', 'l';
5
+ $variants: 'single-line-container', 'multi-line-container';
6
+
7
+ @mixin validationState($state, $rainbowColor, $bgDefault) {
8
+ &[data-validation='#{$state}'] {
9
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$theme-variables, 'sys', $bgDefault, 'background1-level');
10
+ border-color: styles-tokens-element.simple-var(styles-tokens-element.$theme-variables, 'sys', $bgDefault, 'decor-default');
11
+
12
+ &:hover {
13
+ background-color: styles-tokens-element.$sys-neutral-background2-level;
14
+ border-color: styles-tokens-element.simple-var(styles-tokens-element.$theme-variables, 'sys', $rainbowColor, 'decor-hovered');
15
+ }
16
+
17
+ &:not([data-readonly]) {
18
+ &:focus-within,
19
+ &[data-focused] {
20
+ &:not([data-disabled]) {
21
+ @include styles-tokens-element.outline-var(styles-tokens-element.$container-focused-m);
22
+
23
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-background2-level);
24
+ border-color: styles-tokens-element.simple-var(styles-tokens-element.$theme-variables, 'sys', $rainbowColor, 'accent-default');
25
+ outline-color: styles-tokens-element.simple-var(styles-tokens-element.$theme-variables, 'sys', $rainbowColor, 'decor-activated');
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ .container {
33
+ position: relative;
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: space-between;
37
+ box-sizing: border-box;
38
+ border-style: solid;
39
+
40
+ @include validationState('default', 'primary', 'neutral');
41
+ @include validationState('error', 'red', 'red');
42
+ @include validationState('warning', 'yellow', 'yellow');
43
+ @include validationState('success', 'green', 'green');
44
+
45
+ @each $size in $sizes {
46
+ &[data-size='#{$size}'] {
47
+ @include styles-tokens-element.composite-var(styles-tokens-fields.$fields, 'container', $size);
48
+
49
+ &,
50
+ input,
51
+ select,
52
+ textarea,
53
+ span {
54
+ @include styles-tokens-element.composite-var(styles-tokens-element.$theme-variables, 'sans', 'body', $size);
55
+ }
56
+
57
+ @each $variant in $variants {
58
+ &[data-variant='#{$variant}'] {
59
+ @include styles-tokens-element.composite-var(styles-tokens-fields.$fields, $variant, $size);
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ &[data-selectable] {
66
+ &,
67
+ input,
68
+ select,
69
+ textarea,
70
+ span {
71
+ cursor: pointer;
72
+ }
73
+ }
74
+
75
+ &[data-readonly] {
76
+ &,
77
+ input,
78
+ select,
79
+ textarea,
80
+ span {
81
+ cursor: default;
82
+ }
83
+
84
+ &,
85
+ &:hover {
86
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-disabled);
87
+ border-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-disabled);
88
+ }
89
+
90
+ &:focus-within,
91
+ &[data-focused] {
92
+ @include styles-tokens-element.outline-var(styles-tokens-element.$container-focused-m);
93
+
94
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-disabled);
95
+ border-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-disabled);
96
+ outline: none;
97
+ }
98
+ }
99
+
100
+ &[data-disabled] {
101
+ &,
102
+ input,
103
+ select,
104
+ textarea,
105
+ span {
106
+ cursor: not-allowed;
107
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-background);
108
+ }
109
+
110
+ &,
111
+ &:focus-within,
112
+ &[data-focused],
113
+ &:hover {
114
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-background);
115
+ border-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-disabled);
116
+ outline: none;
117
+ }
118
+ }
119
+ }
120
+
121
+ .prefix {
122
+ display: inline-flex;
123
+ flex-shrink: 0;
124
+ color: styles-tokens-element.$sys-neutral-text-disabled;
125
+ }
126
+
127
+ .postfix {
128
+ display: inline-flex;
129
+ flex-shrink: 0;
130
+ gap: styles-tokens-element.$space-fields-postfix-gap;
131
+ }
@@ -0,0 +1,37 @@
1
+ import cn from 'classnames';
2
+
3
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
4
+
5
+ import styles from './styles.module.scss';
6
+
7
+ export type ItemContentProps = WithSupportProps<{
8
+ option: string | number;
9
+ caption?: string | number;
10
+ description?: string;
11
+ disabled?: boolean;
12
+ className?: string;
13
+ }>;
14
+
15
+ export function ItemContent({ caption, description, option, className, disabled, ...rest }: ItemContentProps) {
16
+ return (
17
+ <div
18
+ className={cn(styles.content, className)}
19
+ {...extractSupportProps(rest)}
20
+ data-size='l'
21
+ data-disabled={disabled || undefined}
22
+ >
23
+ <div className={styles.headline}>
24
+ <div className={styles.label}>
25
+ <span data-test-id='list__base-item-option'>{option}</span>
26
+ </div>
27
+ {caption && <span className={styles.caption}>{caption}</span>}
28
+ </div>
29
+
30
+ {description && (
31
+ <div className={styles.description}>
32
+ <span data-test-id='list__base-item-description'>{description}</span>
33
+ </div>
34
+ )}
35
+ </div>
36
+ );
37
+ }
@@ -0,0 +1 @@
1
+ export * from './ItemContent';
@@ -0,0 +1,80 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-dropList';
2
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-element';
3
+
4
+ $sizes: 's', 'm', 'l';
5
+ $typography: (
6
+ 's': (
7
+ 'label': styles-tokens-element.$sans-body-s,
8
+ 'caption': styles-tokens-element.$sans-body-s,
9
+ 'description': styles-tokens-element.$sans-body-s,
10
+ 'separator': styles-tokens-element.$light-label-m,
11
+ ),
12
+ 'm': (
13
+ 'label': styles-tokens-element.$sans-body-m,
14
+ 'caption': styles-tokens-element.$sans-body-s,
15
+ 'description': styles-tokens-element.$sans-body-s,
16
+ 'separator': styles-tokens-element.$light-label-l,
17
+ ),
18
+ 'l': (
19
+ 'label': styles-tokens-element.$sans-body-l,
20
+ 'caption': styles-tokens-element.$sans-body-m,
21
+ 'description': styles-tokens-element.$sans-body-m,
22
+ 'separator': styles-tokens-element.$light-label-l,
23
+ ),
24
+ );
25
+
26
+ .headline {
27
+ display: flex;
28
+ }
29
+
30
+ .label {
31
+ overflow: hidden;
32
+ flex: 1;
33
+ color: styles-tokens-element.$sys-neutral-text-main;
34
+ }
35
+
36
+ .caption {
37
+ overflow: hidden;
38
+ color: styles-tokens-element.$sys-neutral-text-light;
39
+ text-overflow: ellipsis;
40
+ white-space: nowrap;
41
+ }
42
+
43
+ .description {
44
+ color: styles-tokens-element.$sys-neutral-text-support;
45
+ }
46
+
47
+ .content {
48
+ overflow: hidden;
49
+ flex-grow: 1;
50
+ flex-shrink: 1;
51
+ box-sizing: border-box;
52
+
53
+ @each $size in $sizes {
54
+ &[data-size='#{$size}'] {
55
+ .headline {
56
+ @include styles-tokens-element.composite-var(styles-tokens-dropList.$drop-list, 'item', $size, 'headline');
57
+ }
58
+ .label {
59
+ @include styles-tokens-element.composite-var($typography, $size, 'label');
60
+
61
+ display: flex;
62
+ align-items: center;
63
+ }
64
+ .caption {
65
+ @include styles-tokens-element.composite-var($typography, $size, 'caption');
66
+ }
67
+ .description {
68
+ @include styles-tokens-element.composite-var($typography, $size, 'description');
69
+ }
70
+ }
71
+ }
72
+
73
+ &[data-disabled] {
74
+ .label,
75
+ .description,
76
+ .caption {
77
+ color: styles-tokens-element.$sys-neutral-text-disabled;
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,3 @@
1
+ export * from './FieldContainerPrivate';
2
+ export * from './ButtonCopyValue';
3
+ export * from './ItemContent';
@@ -0,0 +1,3 @@
1
+ export * from './useDateField';
2
+ export * from './useFocusHandlers';
3
+ export * from './useHandlers';