@snack-uikit/toggles 0.7.1-preview-104ad22c.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 (73) hide show
  1. package/CHANGELOG.md +357 -0
  2. package/LICENSE +201 -0
  3. package/README.md +169 -0
  4. package/dist/components/Checkbox/Checkbox.d.ts +12 -0
  5. package/dist/components/Checkbox/Checkbox.js +41 -0
  6. package/dist/components/Checkbox/index.d.ts +1 -0
  7. package/dist/components/Checkbox/index.js +1 -0
  8. package/dist/components/Checkbox/styles.module.css +86 -0
  9. package/dist/components/Favorite/Favorite.d.ts +10 -0
  10. package/dist/components/Favorite/Favorite.js +36 -0
  11. package/dist/components/Favorite/index.d.ts +1 -0
  12. package/dist/components/Favorite/index.js +1 -0
  13. package/dist/components/Favorite/styles.module.css +47 -0
  14. package/dist/components/Radio/Radio.d.ts +7 -0
  15. package/dist/components/Radio/Radio.js +24 -0
  16. package/dist/components/Radio/index.d.ts +1 -0
  17. package/dist/components/Radio/index.js +1 -0
  18. package/dist/components/Radio/styles.module.css +90 -0
  19. package/dist/components/Switch/Switch.d.ts +10 -0
  20. package/dist/components/Switch/Switch.js +27 -0
  21. package/dist/components/Switch/index.d.ts +1 -0
  22. package/dist/components/Switch/index.js +1 -0
  23. package/dist/components/Switch/styles.module.css +150 -0
  24. package/dist/components/ToggleGroup/ToggleGroup.d.ts +28 -0
  25. package/dist/components/ToggleGroup/ToggleGroup.js +31 -0
  26. package/dist/components/ToggleGroup/hooks.d.ts +9 -0
  27. package/dist/components/ToggleGroup/hooks.js +23 -0
  28. package/dist/components/ToggleGroup/index.d.ts +2 -0
  29. package/dist/components/ToggleGroup/index.js +2 -0
  30. package/dist/components/TogglePrivate/TogglePrivate.d.ts +22 -0
  31. package/dist/components/TogglePrivate/TogglePrivate.js +38 -0
  32. package/dist/components/TogglePrivate/index.d.ts +1 -0
  33. package/dist/components/TogglePrivate/index.js +1 -0
  34. package/dist/components/TogglePrivate/styles.module.css +38 -0
  35. package/dist/components/index.d.ts +5 -0
  36. package/dist/components/index.js +5 -0
  37. package/dist/components/styles.module.css +0 -0
  38. package/dist/constants.d.ts +16 -0
  39. package/dist/constants.js +20 -0
  40. package/dist/context.d.ts +10 -0
  41. package/dist/context.js +8 -0
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.js +1 -0
  44. package/dist/types.d.ts +47 -0
  45. package/dist/types.js +1 -0
  46. package/dist/utils.d.ts +4 -0
  47. package/dist/utils.js +18 -0
  48. package/package.json +42 -0
  49. package/src/components/Checkbox/Checkbox.tsx +72 -0
  50. package/src/components/Checkbox/index.ts +1 -0
  51. package/src/components/Checkbox/styles.module.scss +77 -0
  52. package/src/components/Favorite/Favorite.tsx +54 -0
  53. package/src/components/Favorite/index.ts +1 -0
  54. package/src/components/Favorite/styles.module.scss +36 -0
  55. package/src/components/Radio/Radio.tsx +30 -0
  56. package/src/components/Radio/index.ts +1 -0
  57. package/src/components/Radio/styles.module.scss +74 -0
  58. package/src/components/Switch/Switch.tsx +50 -0
  59. package/src/components/Switch/index.ts +1 -0
  60. package/src/components/Switch/styles.module.scss +124 -0
  61. package/src/components/ToggleGroup/ToggleGroup.tsx +76 -0
  62. package/src/components/ToggleGroup/hooks.ts +34 -0
  63. package/src/components/ToggleGroup/index.ts +2 -0
  64. package/src/components/TogglePrivate/TogglePrivate.tsx +69 -0
  65. package/src/components/TogglePrivate/index.ts +1 -0
  66. package/src/components/TogglePrivate/styles.module.scss +56 -0
  67. package/src/components/index.ts +5 -0
  68. package/src/components/styles.module.scss +24 -0
  69. package/src/constants.ts +19 -0
  70. package/src/context.ts +17 -0
  71. package/src/index.ts +1 -0
  72. package/src/types.ts +71 -0
  73. package/src/utils.ts +21 -0
@@ -0,0 +1,77 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toggles';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+ @import '../styles.module';
4
+
5
+ $sizes: 's', 'm';
6
+
7
+ $icon-sizes: (
8
+ 's': $icon-xs,
9
+ 'm': $icon-s,
10
+ );
11
+
12
+ .container {
13
+ @include container('checkbox');
14
+ }
15
+
16
+ .box {
17
+ box-sizing: border-box;
18
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'background1-level');
19
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-default');
20
+ border-style: solid; // это наверное должно быть в композитном токене box
21
+
22
+ &[data-checked=false] {
23
+ &[data-focusvisible=true],
24
+ &[data-hover=true] {
25
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'background2-level');
26
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-hovered');
27
+ }
28
+ &[data-disabled=true] {
29
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'background1-level');
30
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
31
+ }
32
+ }
33
+
34
+ &[data-indeterminate=true],
35
+ &[data-checked=true] {
36
+ background-color: simple-var($theme-variables, 'sys', 'primary', 'accent-default');
37
+ border-color: simple-var($theme-variables, 'sys', 'primary', 'accent-default');
38
+ &[data-focusvisible=true],
39
+ &[data-hover=true] {
40
+ background-color: simple-var($theme-variables, 'sys', 'primary', 'accent-hovered');
41
+ border-color: simple-var($theme-variables, 'sys', 'primary', 'accent-hovered');
42
+ }
43
+ &[data-disabled=true] {
44
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
45
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
46
+ }
47
+ }
48
+
49
+ @each $size in $sizes {
50
+ &[data-size='#{$size}'] {
51
+ @include composite-var($toggles, $size, 'checkbox', 'box');
52
+
53
+ svg {
54
+ width: simple-var($icon-sizes, $size) !important; /* stylelint-disable-line declaration-no-important */
55
+ height: simple-var($icon-sizes, $size) !important; /* stylelint-disable-line declaration-no-important */
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ .icon {
62
+ position: absolute;
63
+ top: 0;
64
+ left: 0;
65
+
66
+ display: none;
67
+
68
+ width: 100%;
69
+ height: 100%;
70
+
71
+ color: simple-var($theme-variables, 'sys', 'primary', 'on-accent');
72
+
73
+ &[data-indeterminate=true],
74
+ &[data-checked=true] {
75
+ display: block;
76
+ }
77
+ }
@@ -0,0 +1,54 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { HeartFilledSVG, HeartSVG, StarFilledSVG, StarSVG } from '@snack-uikit/icons';
4
+
5
+ import { FavoriteIcon, Size } from '../../constants';
6
+ import { ToggleProps } from '../../types';
7
+ import { getIconSize, getVisualStateAttributes } from '../../utils';
8
+ import { TogglePrivate } from '../TogglePrivate';
9
+ import styles from './styles.module.scss';
10
+
11
+ const CHECKED_ICONS = {
12
+ [FavoriteIcon.Star]: StarFilledSVG,
13
+ [FavoriteIcon.Heart]: HeartFilledSVG,
14
+ };
15
+
16
+ const UNCHECKED_ICONS = {
17
+ [FavoriteIcon.Star]: StarSVG,
18
+ [FavoriteIcon.Heart]: HeartSVG,
19
+ };
20
+
21
+ export type FavoriteProps = Omit<ToggleProps, 'disabled'> & {
22
+ /** Иконка */
23
+ icon?: FavoriteIcon;
24
+ };
25
+
26
+ export function Favorite({
27
+ inputRef,
28
+ 'data-test-id': dataTestId,
29
+ size = Size.M,
30
+ icon = FavoriteIcon.Heart,
31
+ ...restProps
32
+ }: FavoriteProps) {
33
+ const iconSize = useMemo(() => getIconSize(size), [size]);
34
+
35
+ return (
36
+ <TogglePrivate
37
+ {...restProps}
38
+ data-test-id={dataTestId}
39
+ ref={inputRef}
40
+ size={size}
41
+ render={function Favorite(visualState) {
42
+ const Icon = visualState.checked ? CHECKED_ICONS[icon] : UNCHECKED_ICONS[icon];
43
+ const data = getVisualStateAttributes({ ...visualState, icon });
44
+ return (
45
+ <div className={styles.container} {...data}>
46
+ <Icon className={styles.icon} {...data} size={iconSize} />
47
+ </div>
48
+ );
49
+ }}
50
+ />
51
+ );
52
+ }
53
+
54
+ Favorite.sizes = Size;
@@ -0,0 +1 @@
1
+ export * from './Favorite';
@@ -0,0 +1,36 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toggles';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+ @import '../styles.module';
4
+
5
+ $sizes: 's', 'm';
6
+ $icons: ('star': 'yellow', 'heart': 'red');
7
+
8
+ .container {
9
+ @include container('favourite');
10
+
11
+ outline-color: simple-var($theme-variables, 'sys', 'available', 'complementary');
12
+ &[data-checked=true] {
13
+ outline-color: simple-var($theme-variables, 'sys', 'available', 'complementary');
14
+ }
15
+ }
16
+
17
+ .icon {
18
+ color: simple-var($theme-variables, 'sys', 'neutral', 'text-light');
19
+ &[data-hover=true] {
20
+ color: simple-var($theme-variables, 'sys', 'neutral', 'text-support');
21
+ }
22
+
23
+ @each $icon, $color in $icons {
24
+ &[data-icon='#{$icon}'] {
25
+ &[data-checked=true] {
26
+ color: simple-var($theme-variables, 'sys', $color, 'accent-default');
27
+ &[data-focusvisible=true],
28
+ &[data-hover=true] {
29
+ color: simple-var($theme-variables, 'sys', $color, 'accent-hovered');
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+
36
+ }
@@ -0,0 +1,30 @@
1
+ import { Mode, Size } from '../../constants';
2
+ import { ToggleProps } from '../../types';
3
+ import { getVisualStateAttributes } from '../../utils';
4
+ import { TogglePrivate } from '../TogglePrivate';
5
+ import styles from './styles.module.scss';
6
+
7
+ export type RadioProps = ToggleProps;
8
+
9
+ export function Radio({ inputRef, 'data-test-id': dataTestId, ...restProps }: RadioProps) {
10
+ return (
11
+ <TogglePrivate
12
+ {...restProps}
13
+ data-test-id={dataTestId}
14
+ mode={Mode.Radio}
15
+ ref={inputRef}
16
+ render={function Radio(visualState) {
17
+ const data = getVisualStateAttributes(visualState);
18
+ return (
19
+ <div className={styles.container} {...data}>
20
+ <div className={styles.box} {...data}>
21
+ <div className={styles.flag} {...data} />
22
+ </div>
23
+ </div>
24
+ );
25
+ }}
26
+ />
27
+ );
28
+ }
29
+
30
+ Radio.sizes = Size;
@@ -0,0 +1 @@
1
+ export * from './Radio';
@@ -0,0 +1,74 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toggles';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+ @import '../styles.module';
4
+
5
+ $sizes: 's', 'm';
6
+
7
+ .container {
8
+ @include container('radio');
9
+ }
10
+
11
+ .box {
12
+ position: relative;
13
+
14
+ box-sizing: border-box;
15
+
16
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'background1-level');
17
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-default');
18
+ border-style: solid; // это наверное должно быть в композитном токене box
19
+
20
+ @each $size in $sizes {
21
+ &[data-size='#{$size}'] {
22
+ @include composite-var($toggles, $size, 'radio', 'box');
23
+ }
24
+ }
25
+
26
+ &[data-focusvisible=true],
27
+ &[data-hover=true] {
28
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'background2-level');
29
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-hovered');
30
+ }
31
+
32
+ &[data-disabled=true] {
33
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'background1-level');
34
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
35
+ }
36
+
37
+ &[data-checked=true] {
38
+ border-color: simple-var($theme-variables, 'sys', 'primary', 'accent-default');
39
+ &[data-focusvisible=true],
40
+ &[data-hover=true] {
41
+ border-color: simple-var($theme-variables, 'sys', 'primary', 'accent-hovered');
42
+ }
43
+ &[data-disabled=true] {
44
+ border-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
45
+ }
46
+ }
47
+ }
48
+
49
+ .flag {
50
+ position: absolute;
51
+ top: 50%;
52
+ left: 50%;
53
+ transform: translate(-50%, -50%);
54
+
55
+ background-color: simple-var($theme-variables, 'sys', 'primary', 'accent-default');
56
+
57
+ &[data-hover=true] {
58
+ background-color: simple-var($theme-variables, 'sys', 'primary', 'accent-hovered');
59
+ }
60
+
61
+ &[data-disabled=true] {
62
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'text-disabled');
63
+ }
64
+
65
+ &[data-checked=false] {
66
+ display: none;
67
+ }
68
+
69
+ @each $size in $sizes {
70
+ &[data-size='#{$size}'] {
71
+ @include composite-var($toggles, $size, 'radio', 'flag');
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,50 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { CheckSVG, CrossSVG } from '@snack-uikit/icons';
4
+
5
+ import { Size } from '../../constants';
6
+ import { ToggleProps } from '../../types';
7
+ import { getIconSize, getVisualStateAttributes } from '../../utils';
8
+ import { TogglePrivate } from '../TogglePrivate';
9
+ import styles from './styles.module.scss';
10
+
11
+ export type SwitchProps = ToggleProps & {
12
+ /** Показывать ли иконку в переключателе */
13
+ showIcon?: boolean;
14
+ };
15
+
16
+ export function Switch({ inputRef, 'data-test-id': dataTestId, showIcon, size = Size.M, ...restProps }: SwitchProps) {
17
+ const iconSize = useMemo(() => getIconSize(size), [size]);
18
+
19
+ return (
20
+ <TogglePrivate
21
+ {...restProps}
22
+ size={size}
23
+ data-test-id={dataTestId}
24
+ ref={inputRef}
25
+ render={function Switch(visualState) {
26
+ const data = getVisualStateAttributes(visualState);
27
+ return (
28
+ <div className={styles.container} {...data}>
29
+ <div className={styles.box} {...data} />
30
+ <div className={styles.containerFlag} {...data}>
31
+ <div className={styles.flag} {...data} />
32
+ {showIcon && (
33
+ <>
34
+ <div className={styles.flag_icon_off} {...data}>
35
+ <CrossSVG size={iconSize} />
36
+ </div>
37
+ <div className={styles.flag_icon_on} {...data}>
38
+ <CheckSVG size={iconSize} />
39
+ </div>
40
+ </>
41
+ )}
42
+ </div>
43
+ </div>
44
+ );
45
+ }}
46
+ />
47
+ );
48
+ }
49
+
50
+ Switch.sizes = Size;
@@ -0,0 +1 @@
1
+ export * from './Switch';
@@ -0,0 +1,124 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toggles';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+ @import '../styles.module';
4
+
5
+ $sizes: 's', 'm';
6
+ $switchAnimationDuration: .1s;
7
+
8
+ .container {
9
+ @include container('switch');
10
+ }
11
+
12
+ .box {
13
+ position: relative;
14
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'accent-default');
15
+
16
+ @each $size in $sizes {
17
+ &[data-size='#{$size}'] {
18
+ @include composite-var($toggles, $size, 'switch', 'box');
19
+ }
20
+ }
21
+
22
+ &[data-hover=true] {
23
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'accent-hovered');
24
+ }
25
+
26
+ &[data-disabled=true] {
27
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
28
+ }
29
+
30
+ &[data-checked=true] {
31
+ background-color: simple-var($theme-variables, 'sys', 'primary', 'accent-default');
32
+ &[data-hover=true] {
33
+ background-color: simple-var($theme-variables, 'sys', 'primary', 'accent-hovered');
34
+ }
35
+ &[data-disabled=true] {
36
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
37
+ }
38
+ }
39
+ }
40
+
41
+ .containerFlag {
42
+ position: absolute;
43
+ left: 0;
44
+
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+
49
+ transition-timing-function: linear;
50
+ transition-duration: $switchAnimationDuration;
51
+ transition-property: left;
52
+
53
+ @each $size in $sizes {
54
+ &[data-size='#{$size}'] {
55
+ @include composite-var($toggles, $size, 'switch', 'container-flag');
56
+ &[data-checked=true] {
57
+ left: calc(100% - #{simple-var($toggles, $size, 'switch', 'container-flag', 'width')});
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ .flag {
64
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'on-accent');
65
+
66
+ @each $size in $sizes {
67
+ &[data-size='#{$size}'] {
68
+ @include composite-var($toggles, $size, 'switch', 'flag');
69
+ }
70
+ }
71
+
72
+ &[data-disabled=true] {
73
+ background-color: simple-var($theme-variables, 'sys', 'neutral', 'text-disabled');
74
+ }
75
+ }
76
+
77
+ @mixin flagIcon {
78
+ position: absolute;
79
+ top: 50%;
80
+ left: 50%;
81
+ transform: translate(-50%, -50%);
82
+
83
+ display: flex;
84
+
85
+ color: simple-var($theme-variables, 'sys', 'neutral', 'accent-default');
86
+
87
+ fill: transparent;
88
+
89
+ transition: opacity $switchAnimationDuration linear;
90
+
91
+ &[data-hover=true] {
92
+ color: simple-var($theme-variables, 'sys', 'neutral', 'accent-hovered');
93
+ }
94
+
95
+ &[data-checked=true] {
96
+ color: simple-var($theme-variables, 'sys', 'primary', 'accent-default');
97
+ &[data-hover=true] {
98
+ color: simple-var($theme-variables, 'sys', 'primary', 'accent-hovered');
99
+ }
100
+ }
101
+
102
+ &[data-disabled=true] {
103
+ color: simple-var($theme-variables, 'sys', 'neutral', 'decor-disabled');
104
+ }
105
+
106
+ }
107
+
108
+ .flag_icon_off {
109
+ @include flagIcon();
110
+
111
+ opacity: 1;
112
+ &[data-checked=true] {
113
+ opacity: 0;
114
+ }
115
+ }
116
+
117
+ .flag_icon_on {
118
+ @include flagIcon();
119
+
120
+ opacity: 0;
121
+ &[data-checked=true] {
122
+ opacity: 1;
123
+ }
124
+ }
@@ -0,0 +1,76 @@
1
+ import { ReactNode, useCallback } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { SelectionMode } from '../../constants';
5
+ import { ToggleGroupContext } from '../../context';
6
+
7
+ export type ToggleGroupProps = {
8
+ /** Вложенный контент */
9
+ children: ReactNode;
10
+ } & (
11
+ | {
12
+ /** Начальное состояние */
13
+ defaultValue?: string;
14
+ /** Controlled состояние */
15
+ value?: string;
16
+ /** Controlled обработчик измения состояния */
17
+ onChange?(value: (string | undefined) | string): void;
18
+ /** Режим выбора */
19
+ selectionMode?: SelectionMode.Single;
20
+ }
21
+ | {
22
+ /** Начальное состояние */
23
+ defaultValue?: string[];
24
+ /** Controlled состояние */
25
+ value?: string[];
26
+ /** Controlled обработчик измения состояния */
27
+ onChange?(value: (string[] | undefined) | string[]): void;
28
+ /** Режим выбора */
29
+ selectionMode?: SelectionMode.Multiple;
30
+ }
31
+ );
32
+
33
+ export function ToggleGroup({
34
+ children,
35
+ value: valueProp,
36
+ onChange: onChangeProp,
37
+ selectionMode = SelectionMode.Single,
38
+ defaultValue,
39
+ }: ToggleGroupProps) {
40
+ const [value, setValue] = useUncontrolledProp<string | string[] | undefined>(valueProp, defaultValue, cb => {
41
+ onChangeProp?.(cb(value));
42
+ });
43
+
44
+ const onChange = useCallback(
45
+ (newValue: string) => {
46
+ if (selectionMode === SelectionMode.Single) {
47
+ return setValue((oldValue: string | undefined) => {
48
+ if (newValue !== oldValue) {
49
+ return newValue;
50
+ }
51
+
52
+ return undefined;
53
+ });
54
+ }
55
+
56
+ return setValue((oldValues: string[] | undefined = []) => {
57
+ if (Array.isArray(oldValues)) {
58
+ if (oldValues.includes(newValue)) {
59
+ return oldValues.filter(oldValue => oldValue !== newValue);
60
+ }
61
+
62
+ return oldValues.concat(newValue);
63
+ }
64
+
65
+ return undefined;
66
+ });
67
+ },
68
+ [selectionMode, setValue],
69
+ );
70
+
71
+ return (
72
+ <ToggleGroupContext.Provider value={{ value, onChange, selectionMode }}>{children}</ToggleGroupContext.Provider>
73
+ );
74
+ }
75
+
76
+ ToggleGroup.selectionModes = SelectionMode;
@@ -0,0 +1,34 @@
1
+ import { useCallback, useMemo } from 'react';
2
+
3
+ import { SelectionMode } from '../../constants';
4
+ import { useToggleGroupContext } from '../../context';
5
+
6
+ type UseToggleGroupProps = {
7
+ value: string;
8
+ };
9
+
10
+ export function useToggleGroup({ value: itemValue }: UseToggleGroupProps) {
11
+ const { value, onChange, selectionMode } = useToggleGroupContext();
12
+
13
+ const isChecked = useMemo(() => {
14
+ if (typeof value === 'string') {
15
+ return value === itemValue;
16
+ }
17
+
18
+ if (Array.isArray(value)) {
19
+ return value.includes(itemValue);
20
+ }
21
+
22
+ return false;
23
+ }, [value, itemValue]);
24
+
25
+ const handleClick = useCallback(() => {
26
+ onChange?.(itemValue);
27
+ }, [itemValue, onChange]);
28
+
29
+ return {
30
+ isChecked,
31
+ handleClick,
32
+ multipleSelection: selectionMode === SelectionMode.Multiple,
33
+ };
34
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ToggleGroup';
2
+ export * from './hooks';
@@ -0,0 +1,69 @@
1
+ import cn from 'classnames';
2
+ import { forwardRef, useState } from 'react';
3
+ import { useUncontrolledProp } from 'uncontrollable';
4
+
5
+ import { Mode, Size } from '../../constants';
6
+ import { TogglePrivateProps } from '../../types';
7
+ import { getVisualStateAttributes } from '../../utils';
8
+ import styles from './styles.module.scss';
9
+
10
+ export const TogglePrivate = forwardRef<HTMLInputElement, TogglePrivateProps>(function TogglePrivate(
11
+ {
12
+ render,
13
+ checked: checkedProp,
14
+ defaultChecked,
15
+ onChange,
16
+ onBlur,
17
+ onFocus,
18
+ disabled,
19
+ className,
20
+ size = Size.M,
21
+ mode = Mode.Checkbox,
22
+ 'data-test-id': testId,
23
+ ...otherProps
24
+ },
25
+ ref,
26
+ ) {
27
+ const [checked, setChecked] = useUncontrolledProp(checkedProp, Boolean(defaultChecked), onChange);
28
+ const [focusVisible, setFocusVisible] = useState(false);
29
+ const [hover, setHover] = useState(false);
30
+
31
+ const visualState = {
32
+ disabled: Boolean(disabled),
33
+ focusVisible,
34
+ checked,
35
+ hover,
36
+ size,
37
+ };
38
+
39
+ return (
40
+ <span
41
+ className={cn(className, styles.toggleLayout)}
42
+ data-size={size}
43
+ data-test-id={testId}
44
+ onMouseEnter={() => setHover(true)}
45
+ onMouseLeave={() => setHover(false)}
46
+ {...getVisualStateAttributes(visualState)}
47
+ >
48
+ {render(visualState)}
49
+ <input
50
+ {...otherProps}
51
+ data-test-id={`${testId}-native-input`}
52
+ ref={ref}
53
+ type={mode}
54
+ className={styles.togglePrivate}
55
+ checked={checked}
56
+ disabled={disabled}
57
+ onChange={e => setChecked(e.target.checked)}
58
+ onFocus={event => {
59
+ setFocusVisible(event.target.matches(':focus-visible'));
60
+ onFocus?.(event);
61
+ }}
62
+ onBlur={event => {
63
+ setFocusVisible(false);
64
+ onBlur?.(event);
65
+ }}
66
+ />
67
+ </span>
68
+ );
69
+ });
@@ -0,0 +1 @@
1
+ export * from './TogglePrivate';