@snack-uikit/toaster 0.8.18 → 0.8.19-preview-9cd1b89a.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 (123) hide show
  1. package/README.md +74 -37
  2. package/dist/cjs/components/ToastSystemEvent/styles.module.css +2 -3
  3. package/dist/cjs/components/ToastUpload/ToastUpload.d.ts +59 -0
  4. package/dist/cjs/components/ToastUpload/ToastUpload.js +131 -0
  5. package/dist/cjs/components/ToastUpload/constants.d.ts +16 -0
  6. package/dist/cjs/components/ToastUpload/constants.js +26 -0
  7. package/dist/cjs/components/ToastUpload/helperComponents/ButtonIcon/ButtonIcon.d.ts +3 -0
  8. package/dist/cjs/components/ToastUpload/helperComponents/ButtonIcon/ButtonIcon.js +32 -0
  9. package/dist/cjs/components/ToastUpload/helperComponents/ButtonIcon/index.d.ts +1 -0
  10. package/dist/cjs/components/ToastUpload/helperComponents/ButtonIcon/index.js +25 -0
  11. package/dist/cjs/components/ToastUpload/helperComponents/ButtonIcon/styles.module.css +32 -0
  12. package/dist/cjs/components/ToastUpload/helperComponents/ButtonTextNeutral/ButtonTextNeutral.d.ts +7 -0
  13. package/dist/cjs/components/ToastUpload/helperComponents/ButtonTextNeutral/ButtonTextNeutral.js +28 -0
  14. package/dist/cjs/components/ToastUpload/helperComponents/ButtonTextNeutral/index.d.ts +1 -0
  15. package/dist/cjs/components/ToastUpload/helperComponents/ButtonTextNeutral/index.js +25 -0
  16. package/dist/cjs/components/ToastUpload/helperComponents/ButtonTextNeutral/styles.module.css +33 -0
  17. package/dist/cjs/components/ToastUpload/helperComponents/FileItem/FileItem.d.ts +5 -0
  18. package/dist/cjs/components/ToastUpload/helperComponents/FileItem/FileItem.js +89 -0
  19. package/dist/cjs/components/ToastUpload/helperComponents/FileItem/index.d.ts +1 -0
  20. package/dist/cjs/components/ToastUpload/helperComponents/FileItem/index.js +25 -0
  21. package/dist/cjs/components/ToastUpload/helperComponents/FileItem/styles.module.css +85 -0
  22. package/dist/cjs/components/ToastUpload/helperComponents/LoadingStatus/LoadingStatus.d.ts +6 -0
  23. package/dist/cjs/components/ToastUpload/helperComponents/LoadingStatus/LoadingStatus.js +42 -0
  24. package/dist/cjs/components/ToastUpload/helperComponents/LoadingStatus/index.d.ts +1 -0
  25. package/dist/cjs/components/ToastUpload/helperComponents/LoadingStatus/index.js +25 -0
  26. package/dist/cjs/components/ToastUpload/helperComponents/LoadingStatus/styles.module.css +3 -0
  27. package/dist/cjs/components/ToastUpload/index.d.ts +1 -0
  28. package/dist/cjs/components/ToastUpload/index.js +25 -0
  29. package/dist/cjs/components/ToastUpload/styles.module.css +110 -0
  30. package/dist/cjs/components/ToastUpload/types.d.ts +55 -0
  31. package/dist/cjs/components/ToastUpload/types.js +5 -0
  32. package/dist/cjs/components/ToastUpload/utils/formatPercent.d.ts +1 -0
  33. package/dist/cjs/components/ToastUpload/utils/formatPercent.js +8 -0
  34. package/dist/cjs/components/ToastUpload/utils/index.d.ts +1 -0
  35. package/dist/cjs/components/ToastUpload/utils/index.js +25 -0
  36. package/dist/cjs/components/ToasterContainer/styles.module.css +9 -9
  37. package/dist/cjs/components/index.d.ts +1 -0
  38. package/dist/cjs/components/index.js +2 -1
  39. package/dist/cjs/constants.d.ts +2 -4
  40. package/dist/cjs/constants.js +10 -2
  41. package/dist/cjs/helperComponents/ButtonAction/styles.module.css +2 -2
  42. package/dist/cjs/helpers.d.ts +4 -0
  43. package/dist/cjs/helpers.js +42 -1
  44. package/dist/cjs/index.d.ts +2 -2
  45. package/dist/cjs/index.js +20 -0
  46. package/dist/cjs/testIds.d.ts +14 -0
  47. package/dist/cjs/testIds.js +16 -1
  48. package/dist/cjs/types.d.ts +9 -1
  49. package/dist/esm/components/ToastSystemEvent/styles.module.css +2 -3
  50. package/dist/esm/components/ToastUpload/ToastUpload.d.ts +59 -0
  51. package/dist/esm/components/ToastUpload/ToastUpload.js +45 -0
  52. package/dist/esm/components/ToastUpload/constants.d.ts +16 -0
  53. package/dist/esm/components/ToastUpload/constants.js +20 -0
  54. package/dist/esm/components/ToastUpload/helperComponents/ButtonIcon/ButtonIcon.d.ts +3 -0
  55. package/dist/esm/components/ToastUpload/helperComponents/ButtonIcon/ButtonIcon.js +18 -0
  56. package/dist/esm/components/ToastUpload/helperComponents/ButtonIcon/index.d.ts +1 -0
  57. package/dist/esm/components/ToastUpload/helperComponents/ButtonIcon/index.js +1 -0
  58. package/dist/esm/components/ToastUpload/helperComponents/ButtonIcon/styles.module.css +32 -0
  59. package/dist/esm/components/ToastUpload/helperComponents/ButtonTextNeutral/ButtonTextNeutral.d.ts +7 -0
  60. package/dist/esm/components/ToastUpload/helperComponents/ButtonTextNeutral/ButtonTextNeutral.js +5 -0
  61. package/dist/esm/components/ToastUpload/helperComponents/ButtonTextNeutral/index.d.ts +1 -0
  62. package/dist/esm/components/ToastUpload/helperComponents/ButtonTextNeutral/index.js +1 -0
  63. package/dist/esm/components/ToastUpload/helperComponents/ButtonTextNeutral/styles.module.css +33 -0
  64. package/dist/esm/components/ToastUpload/helperComponents/FileItem/FileItem.d.ts +5 -0
  65. package/dist/esm/components/ToastUpload/helperComponents/FileItem/FileItem.js +26 -0
  66. package/dist/esm/components/ToastUpload/helperComponents/FileItem/index.d.ts +1 -0
  67. package/dist/esm/components/ToastUpload/helperComponents/FileItem/index.js +1 -0
  68. package/dist/esm/components/ToastUpload/helperComponents/FileItem/styles.module.css +85 -0
  69. package/dist/esm/components/ToastUpload/helperComponents/LoadingStatus/LoadingStatus.d.ts +6 -0
  70. package/dist/esm/components/ToastUpload/helperComponents/LoadingStatus/LoadingStatus.js +16 -0
  71. package/dist/esm/components/ToastUpload/helperComponents/LoadingStatus/index.d.ts +1 -0
  72. package/dist/esm/components/ToastUpload/helperComponents/LoadingStatus/index.js +1 -0
  73. package/dist/esm/components/ToastUpload/helperComponents/LoadingStatus/styles.module.css +3 -0
  74. package/dist/esm/components/ToastUpload/index.d.ts +1 -0
  75. package/dist/esm/components/ToastUpload/index.js +1 -0
  76. package/dist/esm/components/ToastUpload/styles.module.css +110 -0
  77. package/dist/esm/components/ToastUpload/types.d.ts +55 -0
  78. package/dist/esm/components/ToastUpload/types.js +1 -0
  79. package/dist/esm/components/ToastUpload/utils/formatPercent.d.ts +1 -0
  80. package/dist/esm/components/ToastUpload/utils/formatPercent.js +1 -0
  81. package/dist/esm/components/ToastUpload/utils/index.d.ts +1 -0
  82. package/dist/esm/components/ToastUpload/utils/index.js +1 -0
  83. package/dist/esm/components/ToasterContainer/styles.module.css +9 -9
  84. package/dist/esm/components/index.d.ts +1 -0
  85. package/dist/esm/components/index.js +1 -0
  86. package/dist/esm/constants.d.ts +2 -4
  87. package/dist/esm/constants.js +8 -0
  88. package/dist/esm/helperComponents/ButtonAction/styles.module.css +2 -2
  89. package/dist/esm/helpers.d.ts +4 -0
  90. package/dist/esm/helpers.js +42 -1
  91. package/dist/esm/index.d.ts +2 -2
  92. package/dist/esm/index.js +1 -0
  93. package/dist/esm/testIds.d.ts +14 -0
  94. package/dist/esm/testIds.js +15 -0
  95. package/dist/esm/types.d.ts +9 -1
  96. package/package.json +10 -6
  97. package/src/components/ToastSystemEvent/styles.module.scss +2 -2
  98. package/src/components/ToastUpload/ToastUpload.tsx +182 -0
  99. package/src/components/ToastUpload/constants.ts +26 -0
  100. package/src/components/ToastUpload/helperComponents/ButtonIcon/ButtonIcon.tsx +10 -0
  101. package/src/components/ToastUpload/helperComponents/ButtonIcon/index.ts +1 -0
  102. package/src/components/ToastUpload/helperComponents/ButtonIcon/styles.module.scss +46 -0
  103. package/src/components/ToastUpload/helperComponents/ButtonTextNeutral/ButtonTextNeutral.tsx +23 -0
  104. package/src/components/ToastUpload/helperComponents/ButtonTextNeutral/index.ts +1 -0
  105. package/src/components/ToastUpload/helperComponents/ButtonTextNeutral/styles.module.scss +41 -0
  106. package/src/components/ToastUpload/helperComponents/FileItem/FileItem.tsx +85 -0
  107. package/src/components/ToastUpload/helperComponents/FileItem/index.ts +1 -0
  108. package/src/components/ToastUpload/helperComponents/FileItem/styles.module.scss +93 -0
  109. package/src/components/ToastUpload/helperComponents/LoadingStatus/LoadingStatus.tsx +38 -0
  110. package/src/components/ToastUpload/helperComponents/LoadingStatus/index.ts +1 -0
  111. package/src/components/ToastUpload/helperComponents/LoadingStatus/styles.module.scss +8 -0
  112. package/src/components/ToastUpload/index.ts +1 -0
  113. package/src/components/ToastUpload/styles.module.scss +123 -0
  114. package/src/components/ToastUpload/types.ts +62 -0
  115. package/src/components/ToastUpload/utils/formatPercent.ts +1 -0
  116. package/src/components/ToastUpload/utils/index.ts +1 -0
  117. package/src/components/ToasterContainer/styles.module.scss +1 -1
  118. package/src/components/index.ts +1 -0
  119. package/src/constants.ts +9 -1
  120. package/src/helpers.tsx +38 -0
  121. package/src/index.ts +2 -10
  122. package/src/testIds.ts +17 -0
  123. package/src/types.ts +7 -1
@@ -0,0 +1,182 @@
1
+ import cn from 'classnames';
2
+ import { MouseEvent } from 'react';
3
+ import { ToastContentProps as RtToastContentProps } from 'react-toastify';
4
+ import { useUncontrolledProp } from 'uncontrollable';
5
+
6
+ import { ChevronDownSVG, ChevronUpSVG, CrossSVG } from '@snack-uikit/icons';
7
+ import { useLocale } from '@snack-uikit/locale';
8
+ import { ProgressBar } from '@snack-uikit/progress-bar';
9
+ import { Scroll } from '@snack-uikit/scroll';
10
+ import { TruncateString } from '@snack-uikit/truncate-string';
11
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
12
+
13
+ import { TOAST_UPLOAD_TEST_IDS } from '../../testIds';
14
+ import { progressBarAppearanceByStatus } from './constants';
15
+ import { ButtonIcon } from './helperComponents/ButtonIcon';
16
+ import { ButtonTextNeutral, ButtonTextNeutralProps } from './helperComponents/ButtonTextNeutral';
17
+ import { FileItem } from './helperComponents/FileItem';
18
+ import { LoadingStatus } from './helperComponents/LoadingStatus';
19
+ import styles from './styles.module.scss';
20
+ import { ToastUploadStatus, UploadActions, UploadItem } from './types';
21
+ import { formatPercent } from './utils';
22
+
23
+ export type ToastUploadProps = Partial<RtToastContentProps> &
24
+ WithSupportProps<{
25
+ /**
26
+ * Общий статус загрузки
27
+ */
28
+ status: ToastUploadStatus;
29
+ /**
30
+ * Заголовок тостера
31
+ */
32
+ title?: string;
33
+ /**
34
+ * Описание статуса загрузки
35
+ */
36
+ description: string;
37
+ /**
38
+ * Общий прогресс загрузки
39
+ */
40
+ progress: {
41
+ current: number;
42
+ total: number;
43
+ };
44
+ /**
45
+ * Закрыть тостер
46
+ */
47
+ onCloseClick?(e: MouseEvent<HTMLButtonElement>, close?: () => void): void;
48
+ /**
49
+ * Показывать кнопку закрытия тостера
50
+ */
51
+ closable?: boolean;
52
+ /**
53
+ * CSS-класс контейнера
54
+ */
55
+ className?: string;
56
+ /**
57
+ * Загружаемые элементы
58
+ */
59
+ files: UploadItem[];
60
+ /**
61
+ * Тостер свернут/развернут
62
+ */
63
+ collapsed?: boolean;
64
+ /**
65
+ * Развернуть/свернуть тостер
66
+ */
67
+ onCollapsed?: (collapsed: boolean) => void;
68
+ /**
69
+ * Экшены для управления загрузкой
70
+ */
71
+ generalActions: Omit<UploadActions, 'onCancel'>;
72
+ /**
73
+ * Отмена всей загрузки
74
+ */
75
+ cancelButton?: ButtonTextNeutralProps;
76
+ }>;
77
+
78
+ export function ToastUpload({
79
+ status,
80
+ title,
81
+ description,
82
+ closeToast,
83
+ onCloseClick,
84
+ closable,
85
+ className,
86
+ files,
87
+ collapsed,
88
+ onCollapsed,
89
+ generalActions,
90
+ cancelButton,
91
+ progress,
92
+ ...rest
93
+ }: ToastUploadProps) {
94
+ const [isCollapsed, setIsCollapsed] = useUncontrolledProp(collapsed, true, onCollapsed);
95
+
96
+ const { t } = useLocale('ToastUpload');
97
+
98
+ const showingTitle = title || t(`title.${status}`);
99
+
100
+ const handleCloseClick = (e: MouseEvent<HTMLButtonElement>) => {
101
+ e.stopPropagation();
102
+ onCloseClick ? onCloseClick(e, closeToast) : closeToast?.();
103
+ };
104
+
105
+ const handleCollapseClick = (e: MouseEvent<HTMLButtonElement>) => {
106
+ e.stopPropagation();
107
+ setIsCollapsed(!isCollapsed);
108
+ };
109
+
110
+ const progressPercent = Math.round((progress.current / progress.total) * 100);
111
+ const isErrorUploaded = status === 'errorUploaded';
112
+
113
+ return (
114
+ <div
115
+ className={cn(styles.container, className)}
116
+ {...extractSupportProps(rest)}
117
+ data-collapsed={isCollapsed || undefined}
118
+ >
119
+ <div className={styles.titleLine}>
120
+ <div className={styles.titleLineBody}>
121
+ <div className={styles.title} data-test-id={TOAST_UPLOAD_TEST_IDS.title}>
122
+ {showingTitle}
123
+ </div>
124
+
125
+ {cancelButton && <ButtonTextNeutral {...cancelButton} />}
126
+
127
+ <ButtonIcon onClick={handleCollapseClick} data-test-id={TOAST_UPLOAD_TEST_IDS.collapseButton}>
128
+ {isCollapsed ? <ChevronUpSVG /> : <ChevronDownSVG />}
129
+ </ButtonIcon>
130
+
131
+ {closable && (
132
+ <ButtonIcon
133
+ className={styles.buttonAction}
134
+ onClick={handleCloseClick}
135
+ data-test-id={TOAST_UPLOAD_TEST_IDS.close}
136
+ >
137
+ <CrossSVG />
138
+ </ButtonIcon>
139
+ )}
140
+ </div>
141
+
142
+ <div className={styles.generalProgress}>
143
+ <div className={styles.statusLine}>
144
+ <div className={styles.statusWrap}>
145
+ <LoadingStatus status={status} actions={generalActions} />
146
+
147
+ <TruncateString
148
+ className={styles.description}
149
+ data-status={status}
150
+ text={description}
151
+ data-test-id={TOAST_UPLOAD_TEST_IDS.description}
152
+ />
153
+ </div>
154
+
155
+ <span className={styles.totalCounter} data-test-id={TOAST_UPLOAD_TEST_IDS.counter}>
156
+ {`${progress.current}/${progress.total}`}
157
+ </span>
158
+
159
+ <span className={styles.totalPercentage} data-test-id={TOAST_UPLOAD_TEST_IDS.progress}>
160
+ {formatPercent(isErrorUploaded ? 0 : progressPercent)}
161
+ </span>
162
+ </div>
163
+
164
+ {!isCollapsed && (
165
+ <ProgressBar
166
+ progress={isErrorUploaded ? 100 : progressPercent}
167
+ size='xs'
168
+ appearance={progressBarAppearanceByStatus[status]}
169
+ data-test-id={TOAST_UPLOAD_TEST_IDS.progressBar}
170
+ />
171
+ )}
172
+ </div>
173
+ </div>
174
+
175
+ <Scroll className={styles.list} size='s' data-test-id={TOAST_UPLOAD_TEST_IDS.list}>
176
+ {files.map(item => (
177
+ <FileItem key={item.id || item.title} item={item} />
178
+ ))}
179
+ </Scroll>
180
+ </div>
181
+ );
182
+ }
@@ -0,0 +1,26 @@
1
+ import { ProgressBarProps } from '@snack-uikit/progress-bar';
2
+
3
+ import { ToastUploadStatus } from './types';
4
+
5
+ export const TOAST_UPLOAD_STATUS = {
6
+ Loading: 'loading',
7
+ Pause: 'pause',
8
+ Error: 'error',
9
+ Uploaded: 'uploaded',
10
+ ErrorUploaded: 'errorUploaded',
11
+ } as const;
12
+
13
+ export const TOAST_UPLOAD_ITEM_STATUS = {
14
+ Loading: 'loading',
15
+ Pause: 'pause',
16
+ Error: 'error',
17
+ Uploaded: 'uploaded',
18
+ } as const;
19
+
20
+ export const progressBarAppearanceByStatus: Record<ToastUploadStatus, ProgressBarProps['appearance']> = {
21
+ pause: 'neutral',
22
+ loading: 'green',
23
+ uploaded: 'green',
24
+ errorUploaded: 'red',
25
+ error: 'red',
26
+ };
@@ -0,0 +1,10 @@
1
+ import cn from 'classnames';
2
+ import { ButtonHTMLAttributes } from 'react';
3
+
4
+ import styles from './styles.module.scss';
5
+
6
+ export type ButtonIconProps = ButtonHTMLAttributes<HTMLButtonElement>;
7
+
8
+ export function ButtonIcon({ className, ...props }: ButtonIconProps) {
9
+ return <button type='button' className={cn(styles.buttonIcon, className)} {...props}></button>;
10
+ }
@@ -0,0 +1 @@
1
+ export * from './ButtonIcon';
@@ -0,0 +1,46 @@
1
+ /* stylelint-disable scss/operator-no-newline-after */
2
+
3
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toaster-toasterSystemEvent';
4
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toaster-toasterUpload';
5
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
6
+ @use '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
7
+
8
+ .buttonIcon {
9
+ @include styles-tokens-toaster-toasterSystemEvent.composite-var(styles-tokens-toaster-toasterSystemEvent.$toaster-system-event-toast-button-button-neutral);
10
+
11
+ cursor: pointer;
12
+
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+
17
+ box-sizing: border-box;
18
+ margin: 0;
19
+ padding: 0;
20
+
21
+ color: styles-theme-variables.$sys-invert-neutral-text-light;
22
+
23
+ background-color: transparent;
24
+ border: none;
25
+
26
+ svg {
27
+ width: styles-tokens-element.$icon-s !important; /* stylelint-disable-line declaration-no-important */
28
+ height: styles-tokens-element.$icon-s !important; /* stylelint-disable-line declaration-no-important */
29
+ }
30
+
31
+ &:hover {
32
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
33
+ }
34
+
35
+ &:active {
36
+ color: styles-theme-variables.$sys-invert-neutral-accent-pressed;
37
+ }
38
+
39
+ &:focus-visible {
40
+ @include styles-theme-variables.outline-var(styles-tokens-element.$container-focused-s);
41
+
42
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
43
+ outline-color: styles-theme-variables.$sys-available-on-complementary;
44
+ outline-offset: styles-theme-variables.$spacing-state-focus-offset;
45
+ }
46
+ }
@@ -0,0 +1,23 @@
1
+ import { MouseEvent } from 'react';
2
+
3
+ import styles from './styles.module.scss';
4
+
5
+ export type ButtonTextNeutralProps = {
6
+ label: string;
7
+ onClick(e: MouseEvent<HTMLButtonElement>): void;
8
+ critical?: boolean;
9
+ };
10
+
11
+ export function ButtonTextNeutral({ label, onClick, critical }: ButtonTextNeutralProps) {
12
+ return (
13
+ <button
14
+ type='button'
15
+ className={styles.buttonTextNeutral}
16
+ onClick={onClick}
17
+ aria-label='action'
18
+ data-critical={critical || undefined}
19
+ >
20
+ {label}
21
+ </button>
22
+ );
23
+ }
@@ -0,0 +1 @@
1
+ export * from './ButtonTextNeutral';
@@ -0,0 +1,41 @@
1
+ /* stylelint-disable scss/operator-no-newline-after */
2
+
3
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toaster-toasterSystemEvent';
4
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toaster-toasterUpload';
5
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
6
+ @use '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
7
+
8
+ .buttonTextNeutral {
9
+ @include styles-tokens-toaster-toasterSystemEvent.composite-var(styles-tokens-toaster-toasterSystemEvent.$toaster-system-event-toast-button-button-neutral);
10
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-m);
11
+
12
+ cursor: pointer;
13
+
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+
18
+ box-sizing: border-box;
19
+ margin: 0;
20
+ padding: 0;
21
+
22
+ color: styles-theme-variables.$sys-invert-neutral-text-light;
23
+
24
+ background-color: transparent;
25
+ border: none;
26
+
27
+ &:hover {
28
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
29
+ }
30
+
31
+ &:active {
32
+ color: styles-theme-variables.$sys-invert-neutral-accent-pressed;
33
+ }
34
+
35
+ &:focus-visible {
36
+ @include styles-theme-variables.outline-var(styles-tokens-element.$container-focused-s);
37
+
38
+ outline-color: styles-theme-variables.$sys-available-on-complementary;
39
+ outline-offset: styles-theme-variables.$spacing-state-focus-offset;
40
+ }
41
+ }
@@ -0,0 +1,85 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { CrossSVG } from '@snack-uikit/icons';
4
+ import { Link } from '@snack-uikit/link';
5
+ import { ProgressBar } from '@snack-uikit/progress-bar';
6
+ import { TruncateString } from '@snack-uikit/truncate-string';
7
+
8
+ import { TOAST_UPLOAD_TEST_IDS } from '../../../../testIds';
9
+ import { progressBarAppearanceByStatus } from '../../constants';
10
+ import { UploadItem } from '../../types';
11
+ import { formatPercent } from '../../utils';
12
+ import { ButtonIcon } from '../ButtonIcon';
13
+ import { LoadingStatus } from '../LoadingStatus';
14
+ import styles from './styles.module.scss';
15
+
16
+ export type FileItemProps = { item: UploadItem };
17
+
18
+ export function FileItem({ item: initItem }: FileItemProps) {
19
+ const [item, setItem] = useState(initItem);
20
+
21
+ const showLink = item.status === 'uploaded' && item.link;
22
+
23
+ const showCancelButton = !showLink;
24
+
25
+ useEffect(() => {
26
+ if (initItem.subscribeToState) {
27
+ return initItem.subscribeToState(newValue => setItem(prev => ({ ...prev, ...newValue })));
28
+ }
29
+
30
+ return setItem(initItem);
31
+ }, [initItem]);
32
+
33
+ const isError = item.status === 'error';
34
+
35
+ return (
36
+ <div className={styles.fileLine} data-test-id={TOAST_UPLOAD_TEST_IDS.uploadItem}>
37
+ <div className={styles.fileHeadLine}>
38
+ <TruncateString text={item.title} className={styles.fileTitle} maxLines={1} />
39
+
40
+ {showLink && (
41
+ <Link
42
+ text={item.link?.text}
43
+ href={item.link?.href}
44
+ size='s'
45
+ truncateVariant='end'
46
+ onClick={item.link?.onClick}
47
+ appearance='invert-neutral'
48
+ textMode='accent'
49
+ data-test-id={TOAST_UPLOAD_TEST_IDS.uploadItemLink}
50
+ />
51
+ )}
52
+
53
+ {showCancelButton && (
54
+ <ButtonIcon onClick={item.actions.onCancel} data-test-id={TOAST_UPLOAD_TEST_IDS.uploadItemCancel}>
55
+ <CrossSVG />
56
+ </ButtonIcon>
57
+ )}
58
+ </div>
59
+
60
+ <ProgressBar
61
+ progress={isError ? 100 : item.progress}
62
+ size='xs'
63
+ appearance={progressBarAppearanceByStatus[item.status]}
64
+ />
65
+
66
+ <div className={styles.fileStatusLine}>
67
+ <div className={styles.fileStatusWrap}>
68
+ <LoadingStatus status={item.status} actions={item.actions} />
69
+
70
+ <span className={styles.fileStatusDescription} data-status={item.status}>
71
+ {item.statusLabel}
72
+ </span>
73
+ </div>
74
+
75
+ <span className={styles.fileSize} data-status={item.status}>
76
+ {item.size}
77
+ </span>
78
+
79
+ <span className={styles.fileStatusPercentage} data-status={item.status}>
80
+ {formatPercent(isError ? 0 : item.progress)}
81
+ </span>
82
+ </div>
83
+ </div>
84
+ );
85
+ }
@@ -0,0 +1 @@
1
+ export * from './FileItem';
@@ -0,0 +1,93 @@
1
+ /* stylelint-disable scss/operator-no-newline-after */
2
+
3
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toaster-toasterSystemEvent';
4
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toaster-toasterUpload';
5
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
6
+ @use '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
7
+
8
+ .fileLine {
9
+ @include styles-theme-variables.composite-var(
10
+ styles-tokens-toaster-toasterUpload.$toaster-upload-file-line-container
11
+ );
12
+
13
+ display: flex;
14
+ flex-direction: column;
15
+ }
16
+
17
+ .fileHeadLine {
18
+ @include styles-theme-variables.composite-var(
19
+ styles-tokens-toaster-toasterUpload.$toaster-upload-file-line-headline
20
+ );
21
+
22
+ display: flex;
23
+ align-items: center;
24
+ }
25
+
26
+ .fileTitle {
27
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-s);
28
+
29
+ flex-grow: 1;
30
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
31
+ }
32
+
33
+
34
+ .fileStatusLine {
35
+ @include styles-theme-variables.composite-var(
36
+ styles-tokens-toaster-toasterUpload.$toaster-upload-status-line-container
37
+ );
38
+
39
+ display: flex;
40
+ align-items: center;
41
+ }
42
+
43
+ .fileStatusWrap {
44
+ @include styles-theme-variables.composite-var(
45
+ styles-tokens-toaster-toasterUpload.$toaster-upload-status-line-status-wrap
46
+ );
47
+
48
+ display: flex;
49
+ flex-grow: 1;
50
+ align-items: center;
51
+ }
52
+
53
+ .fileStatusDescription {
54
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-s);
55
+
56
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
57
+
58
+ &[data-status='loading'] {
59
+ color: styles-theme-variables.$sys-invert-neutral-text-disabled;
60
+ }
61
+
62
+ &[data-status='uploaded'] {
63
+ color: styles-theme-variables.$sys-invert-neutral-text-disabled;
64
+ }
65
+ }
66
+
67
+ .fileSize {
68
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-s);
69
+
70
+ color: styles-theme-variables.$sys-invert-neutral-text-disabled;
71
+
72
+ &[data-status='loading'] {
73
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
74
+ }
75
+
76
+ &[data-status='uploaded'] {
77
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
78
+ }
79
+ }
80
+
81
+ .fileStatusPercentage {
82
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-s);
83
+
84
+ color: styles-theme-variables.$sys-invert-neutral-text-disabled;
85
+
86
+ &[data-status='loading'] {
87
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
88
+ }
89
+
90
+ &[data-status='uploaded'] {
91
+ color: styles-theme-variables.$sys-invert-neutral-text-main;
92
+ }
93
+ }
@@ -0,0 +1,38 @@
1
+ import { CheckFilledSVG, PauseSVG, PlaySVG, UpdateSVG } from '@snack-uikit/icons';
2
+
3
+ import { ToastUploadStatus, UploadActions } from '../../types';
4
+ import { ButtonIcon } from '../ButtonIcon';
5
+ import styles from './styles.module.scss';
6
+
7
+ export type LoadingStatusProps = {
8
+ status: ToastUploadStatus;
9
+ actions: UploadActions;
10
+ };
11
+
12
+ export function LoadingStatus({ status, actions }: LoadingStatusProps) {
13
+ if (status === 'loading') {
14
+ return (
15
+ <ButtonIcon onClick={actions.onPause}>
16
+ <PauseSVG />
17
+ </ButtonIcon>
18
+ );
19
+ }
20
+
21
+ if (status === 'pause') {
22
+ return (
23
+ <ButtonIcon onClick={actions.onContinue}>
24
+ <PlaySVG />
25
+ </ButtonIcon>
26
+ );
27
+ }
28
+
29
+ if (status === 'uploaded') {
30
+ return <CheckFilledSVG className={styles.successIcon} />;
31
+ }
32
+
33
+ return (
34
+ <ButtonIcon onClick={actions.onRetry}>
35
+ <UpdateSVG />
36
+ </ButtonIcon>
37
+ );
38
+ }
@@ -0,0 +1 @@
1
+ export * from './LoadingStatus';
@@ -0,0 +1,8 @@
1
+ /* stylelint-disable scss/operator-no-newline-after */
2
+
3
+ @use '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
4
+
5
+
6
+ .successIcon {
7
+ color: styles-theme-variables.$sys-green-accent-default;
8
+ }
@@ -0,0 +1 @@
1
+ export * from './ToastUpload';