addon-ui 0.5.0 → 0.6.1

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 (47) hide show
  1. package/dist-types/components/Avatar/types.d.ts +1 -0
  2. package/dist-types/components/Button/types.d.ts +3 -1
  3. package/dist-types/components/Tabs/Tabs.d.ts +8 -0
  4. package/dist-types/components/Tabs/TabsContent.d.ts +6 -0
  5. package/dist-types/components/Tabs/TabsList.d.ts +11 -0
  6. package/dist-types/components/Tabs/TabsTrigger.d.ts +11 -0
  7. package/dist-types/components/Tabs/index.d.ts +6 -0
  8. package/dist-types/components/Tabs/types.d.ts +6 -0
  9. package/dist-types/components/Toast/Toast.d.ts +3 -1
  10. package/dist-types/components/Toast/types.d.ts +4 -0
  11. package/dist-types/components/Truncate/Truncate.d.ts +9 -0
  12. package/dist-types/components/Truncate/index.d.ts +2 -0
  13. package/dist-types/components/TruncateList/TruncateList.d.ts +13 -0
  14. package/dist-types/components/TruncateList/index.d.ts +2 -0
  15. package/dist-types/components/index.d.ts +3 -0
  16. package/dist-types/components/types.d.ts +7 -1
  17. package/package.json +8 -6
  18. package/src/components/Avatar/avatar.module.scss +4 -0
  19. package/src/components/Avatar/types.ts +1 -0
  20. package/src/components/Button/button.module.scss +32 -4
  21. package/src/components/Button/types.ts +2 -0
  22. package/src/components/List/list.module.scss +2 -1
  23. package/src/components/ListItem/list-item.module.scss +1 -1
  24. package/src/components/ScrollArea/ScrollArea.tsx +4 -17
  25. package/src/components/ScrollArea/scroll-area.module.scss +10 -2
  26. package/src/components/Tabs/Tabs.tsx +32 -0
  27. package/src/components/Tabs/TabsContent.tsx +21 -0
  28. package/src/components/Tabs/TabsList.tsx +131 -0
  29. package/src/components/Tabs/TabsTrigger.tsx +43 -0
  30. package/src/components/Tabs/index.ts +5 -0
  31. package/src/components/Tabs/tabs.module.scss +166 -0
  32. package/src/components/Tabs/types.ts +5 -0
  33. package/src/components/Tag/tag.module.scss +10 -7
  34. package/src/components/TextField/text-field.module.scss +12 -2
  35. package/src/components/Toast/Toast.tsx +7 -1
  36. package/src/components/Toast/toast.module.scss +56 -13
  37. package/src/components/Toast/types.ts +5 -0
  38. package/src/components/Truncate/Truncate.tsx +115 -0
  39. package/src/components/Truncate/index.ts +1 -0
  40. package/src/components/Truncate/truncate.module.scss +20 -0
  41. package/src/components/TruncateList/TruncateList.tsx +47 -0
  42. package/src/components/TruncateList/index.ts +1 -0
  43. package/src/components/TruncateList/truncate-list.module.scss +20 -0
  44. package/src/components/Viewport/Provider.tsx +1 -1
  45. package/src/components/Viewport/viewport.module.scss +7 -6
  46. package/src/components/index.ts +3 -0
  47. package/src/components/types.ts +12 -0
@@ -0,0 +1,166 @@
1
+ @use "../../styles/mixins" as theme;
2
+
3
+ $root: tabs;
4
+ $radius: var(--tabs-indicator-radius, 10px);
5
+ $border-width: 1px;
6
+
7
+ .#{$root} {
8
+ flex: 1;
9
+ width: 100%;
10
+ display: flex;
11
+ flex-direction: column;
12
+ overflow: hidden;
13
+
14
+ &--reverse {
15
+ flex-direction: column-reverse;
16
+ }
17
+
18
+ &__list {
19
+ position: relative;
20
+ display: flex;
21
+ justify-content: space-between;
22
+ transition: border-bottom-color var(--transition-speed-sm);
23
+
24
+ @include theme.rtl() {
25
+ & {
26
+ flex-direction: row-reverse;
27
+ }
28
+ }
29
+
30
+ &--separator {
31
+ border-bottom: var(--tabs-list-border-width, #{$border-width}) solid
32
+ var(--tabs-list-border-color, var(--separator-color));
33
+
34
+ .#{$root}--reverse & {
35
+ border-bottom: none;
36
+ border-top: var(--tabs-list-border-width, #{$border-width}) solid
37
+ var(--tabs-list-border-color, var(--separator-color));
38
+ }
39
+ }
40
+ }
41
+
42
+ &__trigger {
43
+ display: flex;
44
+ justify-content: center;
45
+ align-items: center;
46
+ width: 100%;
47
+ align-self: center;
48
+ text-align: center;
49
+ cursor: pointer;
50
+ user-select: none;
51
+ padding: var(--tabs-trigger-padding, 10px) 0;
52
+ height: var(--tabs-trigger-height, 40px);
53
+ font-size: var(--tabs-trigger-font-size, 14px);
54
+ font-family: var(--tabs-trigger-font-family, var(--font-family)), sans-serif;
55
+ font-weight: var(--tabs-trigger-font-weight, 400);
56
+ color: var(--tabs-trigger-color, var(--text-primary-color));
57
+ transition:
58
+ color var(--transition-speed-sm) ease,
59
+ font-weight var(--transition-speed-sm) ease;
60
+
61
+ &[data-state="active"] {
62
+ font-weight: var(--tabs-trigger-font-weight-active, 600);
63
+ }
64
+
65
+ &[data-disabled] {
66
+ cursor: default;
67
+ opacity: var(--tabs-trigger-dissabled-opacity, 0.6);
68
+ }
69
+
70
+ &__before,
71
+ &__after {
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ min-width: var(--tabs-trigger-badge-min-width, 18px);
76
+ font-size: var(--tabs-trigger-badge-font-size, 12px);
77
+ font-weight: var(--tabs-trigger-badge-font-weight, 700);
78
+ padding: var(--tabs-trigger-badge-padding, 2px 4px);
79
+ border-radius: var(--tabs-trigger-badge-radius, 4px);
80
+ color: var(--tabs-trigger-badge-color, var(--text-secondary-color));
81
+ background-color: var(--tabs-trigger-badge-bg-color, var(--bg-secondary-color));
82
+ transition:
83
+ background-color var(--transition-speed-sm) ease,
84
+ color var(--transition-speed-sm) ease;
85
+
86
+ .#{$root}__trigger[data-state="active"] & {
87
+ color: var(--tabs-trigger-badge-active-color, white);
88
+ background-color: var(--tabs-trigger-badge-active-bg-color, var(--primary-color));
89
+ }
90
+ }
91
+
92
+ &-wrapper {
93
+ display: flex;
94
+ justify-content: center;
95
+ align-items: center;
96
+ width: 100%;
97
+ gap: var(--tabs-trigger-gap, 4px);
98
+ }
99
+ }
100
+
101
+ &__indicator {
102
+ opacity: 0;
103
+ position: absolute;
104
+ bottom: calc(-1 * var(--tabs-list-border-width, #{$border-width}));
105
+ height: var(--tabs-indicator-size, 4px);
106
+ background-color: var(--tabs-indicator-color, var(--primary-color));
107
+ border-radius: #{$radius} #{$radius} 0 0;
108
+ will-change: opacity, left, width;
109
+ transition:
110
+ opacity var(--transition-speed-sm) ease,
111
+ left var(--transition-speed-sm) ease,
112
+ width var(--transition-speed-sm) ease;
113
+
114
+ .#{$root}--reverse & {
115
+ bottom: auto;
116
+ top: calc(-1 * var(--tabs-list-border-width, #{$border-width}));
117
+ border-radius: 0 0 #{$radius} #{$radius};
118
+ }
119
+
120
+ &--show {
121
+ opacity: 1;
122
+ }
123
+
124
+ &--first {
125
+ @include theme.ltr() {
126
+ &:not(.#{$root}__indicator--rounded_edges) {
127
+ border-top-left-radius: 0;
128
+ border-bottom-left-radius: 0;
129
+ }
130
+ }
131
+
132
+ @include theme.rtl() {
133
+ &:not(.#{$root}__indicator--rounded_edges) {
134
+ border-top-right-radius: 0;
135
+ border-bottom-right-radius: 0;
136
+ }
137
+ }
138
+ }
139
+
140
+ &--last {
141
+ @include theme.ltr() {
142
+ &:not(.#{$root}__indicator--rounded_edges) {
143
+ border-top-right-radius: 0;
144
+ border-bottom-right-radius: 0;
145
+ }
146
+ }
147
+
148
+ @include theme.rtl() {
149
+ &:not(.#{$root}__indicator--rounded_edges) {
150
+ border-top-left-radius: 0;
151
+ border-bottom-left-radius: 0;
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ &__content {
158
+ overflow: hidden;
159
+ display: flex;
160
+ flex-direction: column;
161
+
162
+ &[data-state="active"] {
163
+ flex: 1;
164
+ }
165
+ }
166
+ }
@@ -0,0 +1,5 @@
1
+ export enum TabsColor {
2
+ Primary = "primary",
3
+ Secondary = "secondary",
4
+ Accent = "accent",
5
+ }
@@ -49,21 +49,24 @@ $root: tag;
49
49
  color: var(--tag-outlined-color, var(--tag-color, var(--text-secondary-color)));
50
50
  border: var(--tag-border-width, 1px) solid
51
51
  var(--tag-outlined-border-color, var(--tag-outlined-color, var(--text-secondary-color)));
52
- background: none;
52
+ background: var(--tag-outlined-bg-color, none);
53
53
 
54
54
  &.#{$root}--primary-color {
55
- color: var(--primary-color);
56
- border-color: var(--tag-outlined-border-primary-color, var(--primary-color));
55
+ color: var(--tag-outlined-color-primary-color, var(--primary-color));
56
+ border-color: var(--tag-outlined-border-color-primary-color, var(--primary-color));
57
+ background: var(--tag-outlined-bg-color-primary-color, none);
57
58
  }
58
59
 
59
60
  &.#{$root}--secondary-color {
60
- color: var(--secondary-color);
61
- border-color: var(--tag-outlined-border-secondary-color, var(--secondary-color));
61
+ color: var(--tag-outlined-color-secondary-color, var(--secondary-color));
62
+ border-color: var(--tag-outlined-border-color-secondary-color, var(--secondary-color));
63
+ background: var(--tag-outlined-bg-color-secondary-color, none);
62
64
  }
63
65
 
64
66
  &.#{$root}--accent-color {
65
- color: var(--accent-color);
66
- border-color: var(--tag-outlined-border-accent-color, var(--accent-color));
67
+ color: var(--tag-outlined-color-accent-color, var(--accent-color));
68
+ border-color: var(--tag-outlined-border-color-accent-color, var(--accent-color));
69
+ background: var(--tag-outlined-bg-color-accent-color, none);
67
70
  }
68
71
  }
69
72
 
@@ -1,3 +1,5 @@
1
+ @use "../../styles/mixins" as dir;
2
+
1
3
  $root: text-field;
2
4
 
3
5
  .#{$root} {
@@ -16,6 +18,10 @@ $root: text-field;
16
18
  box-shadow var(--transition-speed-sm),
17
19
  background var(--transition-speed-sm);
18
20
 
21
+ @include dir.rtl {
22
+ flex-direction: row-reverse;
23
+ }
24
+
19
25
  &:has(&__input:focus) {
20
26
  border-color: var(--text-field-focus-border-color, color-mix(in srgb, white 40%, var(--primary-color)));
21
27
  box-shadow: var(--text-field-focus-shadow, 0 0 4px var(--primary-color));
@@ -43,6 +49,10 @@ $root: text-field;
43
49
  &:disabled {
44
50
  cursor: not-allowed;
45
51
  }
52
+
53
+ &::placeholder {
54
+ color: var(--text-field-placeholder-color, var(--text-secondary-color));
55
+ }
46
56
  }
47
57
 
48
58
  &__before,
@@ -107,12 +117,12 @@ $root: text-field;
107
117
 
108
118
  &--medium-size {
109
119
  padding: var(--text-field-padding-md, 10px 14px);
110
- font-size: var(--text-field-font-size-sm, 16px);
120
+ font-size: var(--text-field-font-size-md, 16px);
111
121
  }
112
122
 
113
123
  &--large-size {
114
124
  padding: var(--text-field-padding-lg, 12px 16px);
115
- font-size: var(--text-field-font-size-sm, 18px);
125
+ font-size: var(--text-field-font-size-lg, 18px);
116
126
  }
117
127
 
118
128
  // Accents
@@ -14,7 +14,7 @@ import {IconButton, IconButtonProps} from "../IconButton";
14
14
  import {cloneOrCreateElement} from "../../utils";
15
15
  import {useComponentProps} from "../../providers";
16
16
 
17
- import {ToastSide, ToastRadius, ToastColor} from "./types";
17
+ import {ToastSide, ToastRadius, ToastColor, ToastAnimation} from "./types";
18
18
 
19
19
  import styles from "./toast.module.scss";
20
20
 
@@ -33,6 +33,8 @@ export interface ToastProps extends Omit<ToastRootProps, "title">, Omit<ToastPro
33
33
  radius?: ToastRadius;
34
34
  title?: ReactNode;
35
35
  action?: ReactNode;
36
+ animationIn?: ToastAnimation;
37
+ animationOut?: ToastAnimation;
36
38
  description?: ReactNode;
37
39
  closeIcon?: ReactElement;
38
40
  closeProps?: IconButtonProps;
@@ -54,6 +56,8 @@ const Toast: ForwardRefRenderFunction<HTMLLIElement, ToastProps> = (props, ref)
54
56
  radius,
55
57
  title,
56
58
  action,
59
+ animationIn = ToastAnimation.Slide,
60
+ animationOut = ToastAnimation.Slide,
57
61
  description,
58
62
  fullWidth,
59
63
  sticky,
@@ -87,6 +91,8 @@ const Toast: ForwardRefRenderFunction<HTMLLIElement, ToastProps> = (props, ref)
87
91
  [styles[`toast--${side}`]]: side,
88
92
  [styles[`toast--${color}-color`]]: color,
89
93
  [styles[`toast--${radius}-radius`]]: radius,
94
+ [styles[`toast--${animationIn}-animation-in`]]: animationIn,
95
+ [styles[`toast--${animationOut}-animation-out`]]: animationOut,
90
96
  [styles["toast--sticky"]]: sticky,
91
97
  [styles["toast--full-width"]]: fullWidth,
92
98
  },
@@ -15,27 +15,55 @@ $root: toast;
15
15
  padding: var(--toast-padding, var(--side-padding-xs));
16
16
 
17
17
  &[data-state="open"] {
18
- &.#{$root}--top-left,
19
- &.#{$root}--bottom-left {
20
- animation: slideInLeft var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
18
+ &.#{$root}--opacity-animation-in {
19
+ animation: show var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
21
20
  }
22
21
 
23
- &.#{$root}--top-right,
24
- &.#{$root}--bottom-right {
25
- animation: slideInRight var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
26
- }
22
+ &.#{$root}--slide-animation-in {
23
+ &.#{$root}--top-left,
24
+ &.#{$root}--bottom-left {
25
+ animation: slideInLeft var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
26
+ }
27
27
 
28
- &.#{$root}--top-center {
29
- animation: slideInTop var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
30
- }
28
+ &.#{$root}--top-right,
29
+ &.#{$root}--bottom-right {
30
+ animation: slideInRight var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
31
+ }
31
32
 
32
- &.#{$root}--bottom-center {
33
- animation: slideInBottom var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
33
+ &.#{$root}--top-center {
34
+ animation: slideInTop var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
35
+ }
36
+
37
+ &.#{$root}--bottom-center {
38
+ animation: slideInBottom var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
39
+ }
34
40
  }
35
41
  }
36
42
 
37
43
  &[data-state="closed"] {
38
- animation: hide var(--toast-transition-speed, var(--transition-speed-md)) ease-in;
44
+ &.#{$root}--opacity-animation-out {
45
+ animation: hide var(--toast-transition-speed, var(--transition-speed-md)) ease-in;
46
+ }
47
+
48
+ &.#{$root}--slide-animation-out {
49
+ &.#{$root}--top-left,
50
+ &.#{$root}--bottom-left {
51
+ animation: swipeOutLeft var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
52
+ }
53
+
54
+ &.#{$root}--top-right,
55
+ &.#{$root}--bottom-right {
56
+ animation: swipeOutRight var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
57
+ }
58
+
59
+ &.#{$root}--top-center {
60
+ animation: swipeOutTop var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
61
+ }
62
+
63
+ &.#{$root}--bottom-center {
64
+ animation: swipeOutBottom var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
65
+ }
66
+ }
39
67
  }
40
68
 
41
69
  &[data-swipe="move"] {
@@ -52,6 +80,7 @@ $root: toast;
52
80
  &.#{$root}--bottom-left {
53
81
  animation: swipeOutLeft var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
54
82
  }
83
+
55
84
  &.#{$root}--top-right,
56
85
  &.#{$root}--bottom-right {
57
86
  animation: swipeOutRight var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
@@ -60,6 +89,7 @@ $root: toast;
60
89
  &.#{$root}--top-center {
61
90
  animation: swipeOutTop var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
62
91
  }
92
+
63
93
  &.#{$root}--bottom-center {
64
94
  animation: swipeOutBottom var(--toast-transition-speed, var(--transition-speed-md)) ease-out;
65
95
  }
@@ -68,12 +98,15 @@ $root: toast;
68
98
  &--none-radius {
69
99
  border-radius: 0;
70
100
  }
101
+
71
102
  &--small-radius {
72
103
  border-radius: var(--toast-border-radius, 6px);
73
104
  }
105
+
74
106
  &--medium-radius {
75
107
  border-radius: var(--toast-border-radius, 15px);
76
108
  }
109
+
77
110
  &--large-radius {
78
111
  border-radius: var(--toast-border-radius, 20px);
79
112
  }
@@ -91,6 +124,7 @@ $root: toast;
91
124
  .#{$root}--error-color & {
92
125
  color: var(--toast-error-text-color, white);
93
126
  }
127
+
94
128
  .#{$root}--success-color & {
95
129
  color: var(--toast-success-text-color, white);
96
130
  }
@@ -194,6 +228,15 @@ $root: toast;
194
228
  }
195
229
  }
196
230
 
231
+ @keyframes show {
232
+ from {
233
+ opacity: 0;
234
+ }
235
+ to {
236
+ opacity: 1;
237
+ }
238
+ }
239
+
197
240
  @keyframes slideInRight {
198
241
  from {
199
242
  transform: translateX(100%);
@@ -18,3 +18,8 @@ export enum ToastColor {
18
18
  Error = "error",
19
19
  Success = "success",
20
20
  }
21
+
22
+ export enum ToastAnimation {
23
+ Slide = "slide",
24
+ Opacity = "opacity",
25
+ }
@@ -0,0 +1,115 @@
1
+ import React, {
2
+ ComponentProps,
3
+ forwardRef,
4
+ ForwardRefRenderFunction,
5
+ memo,
6
+ useImperativeHandle,
7
+ useLayoutEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+ import classnames from "classnames";
12
+
13
+ import {useComponentProps} from "../../providers";
14
+
15
+ import styles from "./truncate.module.scss";
16
+
17
+ export interface TruncateProps extends ComponentProps<"span"> {
18
+ text?: string;
19
+ middle?: boolean;
20
+ separator?: string;
21
+ }
22
+
23
+ const trimMiddle = (el: HTMLElement, text: string, separator: string) => {
24
+ const measure = (txt: string) => {
25
+ el.textContent = txt;
26
+ return el.scrollWidth <= el.clientWidth;
27
+ };
28
+
29
+ if (measure(text)) return text;
30
+
31
+ let low = 0;
32
+ let high = text.length - 2;
33
+ let result = "";
34
+
35
+ while (low <= high) {
36
+ const size = Math.floor((low + high) / 2);
37
+ const left = text.slice(0, Math.ceil(size / 2));
38
+ const right = text.slice(text.length - Math.floor(size / 2));
39
+ const trimmed = left + separator + right;
40
+
41
+ if (measure(trimmed)) {
42
+ result = trimmed;
43
+ low = size + 1;
44
+ } else {
45
+ high = size - 1;
46
+ }
47
+ }
48
+
49
+ return result || text.charAt(0) + separator + text.charAt(text.length - 1);
50
+ };
51
+
52
+ const Truncate: ForwardRefRenderFunction<HTMLSpanElement, TruncateProps> = (props, ref) => {
53
+ const {text = "", middle, separator = "...", className, ...other} = {...useComponentProps("truncate"), ...props};
54
+
55
+ const innerRef = useRef<HTMLSpanElement | null>(null);
56
+ const [displayedText, setDisplayedText] = useState(text);
57
+
58
+ useImperativeHandle(ref, () => innerRef.current!, []);
59
+
60
+ useLayoutEffect(() => {
61
+ const el = innerRef.current;
62
+ if (!el || !middle) return;
63
+
64
+ let animationFrameId: number;
65
+ let observer: ResizeObserver | null = null;
66
+
67
+ const measureAndTrim = () => {
68
+ animationFrameId = requestAnimationFrame(() => {
69
+ const newText = trimMiddle(el, text, separator);
70
+ if (newText !== displayedText) {
71
+ setDisplayedText(newText);
72
+ }
73
+ });
74
+ };
75
+
76
+ measureAndTrim();
77
+
78
+ if ("ResizeObserver" in window) {
79
+ observer = new ResizeObserver(() => {
80
+ cancelAnimationFrame(animationFrameId);
81
+ measureAndTrim();
82
+ });
83
+ observer.observe(el);
84
+ }
85
+
86
+ window.addEventListener("resize", measureAndTrim);
87
+
88
+ return () => {
89
+ window.removeEventListener("resize", measureAndTrim);
90
+ observer?.disconnect();
91
+ cancelAnimationFrame(animationFrameId);
92
+ };
93
+ // eslint-disable-next-line react-hooks/exhaustive-deps
94
+ }, [text, separator, middle]);
95
+
96
+ return (
97
+ <span
98
+ ref={innerRef}
99
+ className={classnames(
100
+ styles["truncate"],
101
+ {
102
+ [styles["truncate--middle"]]: middle,
103
+ },
104
+ className
105
+ )}
106
+ {...other}
107
+ >
108
+ {middle ? displayedText : text}
109
+ </span>
110
+ );
111
+ };
112
+
113
+ Truncate.displayName = "Truncate";
114
+
115
+ export default memo(forwardRef(Truncate));
@@ -0,0 +1 @@
1
+ export {default as Truncate, type TruncateProps} from "./Truncate";
@@ -0,0 +1,20 @@
1
+ @use "../../styles/mixins" as theme;
2
+
3
+ .truncate {
4
+ display: block;
5
+ width: 100%;
6
+ white-space: nowrap;
7
+ overflow: hidden;
8
+ text-overflow: ellipsis;
9
+
10
+ &--middle {
11
+ text-overflow: clip;
12
+
13
+ padding-right: var(--truncate-around-space, 8px);
14
+
15
+ @include theme.rtl() {
16
+ padding-right: 0;
17
+ padding-left: var(--truncate-around-space, 8px);
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,47 @@
1
+ import React, {forwardRef, memo, useCallback} from "react";
2
+ import classnames from "classnames";
3
+ import {OverflowList, OverflowListProps} from "react-responsive-overflow-list";
4
+
5
+ import {useComponentProps} from "../../providers";
6
+
7
+ import styles from "./truncate-list.module.scss";
8
+ import {Tag} from "../Tag";
9
+
10
+ export type TruncateListProps<T = unknown> = OverflowListProps<T> & {
11
+ counterClassName?: string;
12
+ };
13
+
14
+ function TruncateListBase<T>(props: TruncateListProps<T>, ref: React.Ref<HTMLDivElement>) {
15
+ const {renderOverflow, className, counterClassName, ...other} = {...useComponentProps("truncateList"), ...props};
16
+
17
+ const RenderOverflow = useCallback(
18
+ (hiddenItems: T[]) => {
19
+ if (renderOverflow) return renderOverflow(hiddenItems);
20
+
21
+ return (
22
+ <Tag className={classnames(styles["truncate-list__counter"], counterClassName)}>
23
+ {`+${hiddenItems.length}`}
24
+ </Tag>
25
+ );
26
+ },
27
+ [renderOverflow, counterClassName]
28
+ );
29
+
30
+ return (
31
+ <OverflowList
32
+ ref={ref}
33
+ renderOverflow={RenderOverflow}
34
+ className={classnames(styles["truncate-list"], className)}
35
+ {...(other as OverflowListProps<T>)}
36
+ />
37
+ );
38
+ }
39
+
40
+ const TruncateList = memo(forwardRef(TruncateListBase)) as unknown as {
41
+ <T>(props: TruncateListProps<T> & {ref?: React.Ref<HTMLDivElement>}): React.ReactElement | null;
42
+ displayName?: string;
43
+ };
44
+
45
+ TruncateList.displayName = "TruncateList";
46
+
47
+ export default TruncateList;
@@ -0,0 +1 @@
1
+ export {default as TruncateList, type TruncateListProps} from "./TruncateList";
@@ -0,0 +1,20 @@
1
+ @use "../../styles/mixins" as theme;
2
+
3
+ .truncate-list {
4
+ gap: var(--truncate-list-gap, 8px);
5
+
6
+ @include theme.rtl() {
7
+ flex-direction: row-reverse;
8
+ }
9
+
10
+ &__counter {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ border-radius: var(--truncate-list-counter-br-radius, 9999px);
15
+ background: var(--truncate-list-counter-bg-color, var(--bg-secondary-color));
16
+ padding: var(--truncate-list-counter-padding, 0px 4px);
17
+ color: var(--truncate-list-counter-color, var(--text-secondary-color));
18
+ font-size: var(--truncate-list-counter-font-size, inherit);
19
+ }
20
+ }
@@ -49,7 +49,7 @@ const Provider: ForwardRefRenderFunction<HTMLDivElement, PropsWithChildren<Viewp
49
49
  setSizes,
50
50
  setMode,
51
51
  resetSizes,
52
- withTransition
52
+ withTransition,
53
53
  }),
54
54
  [mode, setSizes, setMode, resetSizes]
55
55
  );
@@ -8,12 +8,13 @@
8
8
  max-height: var(--viewport-max-height);
9
9
 
10
10
  &--transition {
11
- transition: height var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
12
- width var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
13
- min-height var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
14
- min-width var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
15
- max-height var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
16
- max-width var(--transition-viewport, var(--transition-speed-md)) ease-in-out;
11
+ transition:
12
+ height var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
13
+ width var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
14
+ min-height var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
15
+ min-width var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
16
+ max-height var(--transition-viewport, var(--transition-speed-md)) ease-in-out,
17
+ max-width var(--transition-viewport, var(--transition-speed-md)) ease-in-out;
17
18
  }
18
19
 
19
20
  &--expanded {
@@ -16,11 +16,14 @@ export * from "./Odometer";
16
16
  export * from "./ScrollArea";
17
17
  export * from "./SvgSprite";
18
18
  export * from "./Switch";
19
+ export * from "./Tabs";
19
20
  export * from "./Tag";
20
21
  export * from "./TextArea";
21
22
  export * from "./TextField";
22
23
  export * from "./Toast";
23
24
  export * from "./Tooltip";
25
+ export * from "./Truncate";
26
+ export * from "./TruncateList";
24
27
  export * from "./View";
25
28
  export * from "./ViewDrawer";
26
29
  export * from "./ViewModal";