addon-ui 0.7.1 → 0.9.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 (33) hide show
  1. package/README.md +1 -0
  2. package/dist-types/components/Highlight/Highlight.d.ts +2 -1
  3. package/dist-types/components/Select/Select.d.ts +6 -0
  4. package/dist-types/components/Select/SelectContent.d.ts +14 -0
  5. package/dist-types/components/Select/SelectIcon.d.ts +6 -0
  6. package/dist-types/components/Select/SelectItem.d.ts +9 -0
  7. package/dist-types/components/Select/SelectTrigger.d.ts +14 -0
  8. package/dist-types/components/Select/SelectValue.d.ts +6 -0
  9. package/dist-types/components/Select/index.d.ts +7 -0
  10. package/dist-types/components/Truncate/Truncate.d.ts +2 -0
  11. package/dist-types/components/index.d.ts +1 -0
  12. package/dist-types/components/types.d.ts +8 -2
  13. package/package.json +10 -5
  14. package/src/components/Accordion/accordion.module.scss +1 -0
  15. package/src/components/Checkbox/checkbox.module.scss +1 -1
  16. package/src/components/Highlight/Highlight.tsx +22 -6
  17. package/src/components/Highlight/highlight.module.scss +43 -39
  18. package/src/components/Modal/modal.module.scss +1 -0
  19. package/src/components/Select/Select.tsx +19 -0
  20. package/src/components/Select/SelectContent.tsx +84 -0
  21. package/src/components/Select/SelectIcon.tsx +19 -0
  22. package/src/components/Select/SelectItem.tsx +41 -0
  23. package/src/components/Select/SelectTrigger.tsx +71 -0
  24. package/src/components/Select/SelectValue.tsx +19 -0
  25. package/src/components/Select/index.ts +6 -0
  26. package/src/components/Select/select.module.scss +150 -0
  27. package/src/components/Tabs/tabs.module.scss +4 -1
  28. package/src/components/TextArea/TextArea.tsx +1 -1
  29. package/src/components/TextField/TextField.tsx +1 -1
  30. package/src/components/Truncate/Truncate.tsx +26 -23
  31. package/src/components/Truncate/truncate.module.scss +10 -0
  32. package/src/components/index.ts +1 -0
  33. package/src/components/types.ts +13 -1
package/README.md CHANGED
@@ -69,6 +69,7 @@ This library now ships with dedicated documentation files for each component in
69
69
  - [Modal](docs/Modal.md)
70
70
  - [Odometer](docs/Odometer.md) (component + useOdometer hook)
71
71
  - [ScrollArea](docs/ScrollArea.md)
72
+ - [Select](docs/Select.md)
72
73
  - [SvgSprite](docs/SvgSprite.md)
73
74
  - [Switch](docs/Switch.md)
74
75
  - [Tabs](docs/Tabs.md) (includes Tabs, TabsList, TabsTrigger, TabsContent)
@@ -1,8 +1,9 @@
1
1
  import React from "react";
2
2
  import { HighlighterProps } from "react-highlight-words";
3
3
  import { HighlightColor } from "./types";
4
- export interface HighlightProps extends HighlighterProps {
4
+ export interface HighlightProps extends Omit<HighlighterProps, "searchWords"> {
5
5
  color?: HighlightColor;
6
+ searchWords?: string | RegExp | (string | RegExp)[];
6
7
  }
7
8
  declare const _default: React.NamedExoticComponent<HighlightProps>;
8
9
  export default _default;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import { SelectProps } from "@radix-ui/react-select";
3
+ export { SelectProps };
4
+ declare const _default: React.NamedExoticComponent<SelectProps>;
5
+ export default _default;
6
+ //# sourceMappingURL=Select.d.ts.map
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { SelectContentProps as SelectContentRadixProps, SelectPortalProps, SelectViewportProps } from "@radix-ui/react-select";
3
+ export interface SelectContentProps extends SelectContentRadixProps, SelectPortalProps {
4
+ arrow?: boolean;
5
+ arrowWidth?: number;
6
+ arrowHeight?: number;
7
+ fullWidth?: boolean;
8
+ viewportProps?: SelectViewportProps;
9
+ scrollUpButton?: React.ReactNode;
10
+ scrollDownButton?: React.ReactNode;
11
+ }
12
+ declare const _default: React.NamedExoticComponent<SelectContentProps & React.RefAttributes<HTMLDivElement>>;
13
+ export default _default;
14
+ //# sourceMappingURL=SelectContent.d.ts.map
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import { SelectIconProps } from "@radix-ui/react-select";
3
+ export { SelectIconProps };
4
+ declare const _default: React.NamedExoticComponent<SelectIconProps & React.RefAttributes<HTMLSpanElement>>;
5
+ export default _default;
6
+ //# sourceMappingURL=SelectIcon.d.ts.map
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { SelectItemProps as SelectItemRadixProps } from "@radix-ui/react-select";
3
+ export interface SelectItemProps extends SelectItemRadixProps {
4
+ indicator?: React.ReactNode;
5
+ indicatorClassname?: string;
6
+ }
7
+ declare const _default: React.NamedExoticComponent<SelectItemProps & React.RefAttributes<HTMLDivElement>>;
8
+ export default _default;
9
+ //# sourceMappingURL=SelectItem.d.ts.map
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { SelectIconProps, SelectTriggerProps as SelectTriggerRadixProps, SelectValueProps } from "@radix-ui/react-select";
3
+ export interface SelectTriggerProps extends SelectTriggerRadixProps {
4
+ center?: boolean;
5
+ fullWidth?: boolean;
6
+ ellipsis?: boolean;
7
+ icon?: React.ReactNode;
8
+ placeholder?: React.ReactNode;
9
+ valueProps?: SelectValueProps;
10
+ iconProps?: SelectIconProps;
11
+ }
12
+ declare const _default: React.NamedExoticComponent<SelectTriggerProps & React.RefAttributes<HTMLButtonElement>>;
13
+ export default _default;
14
+ //# sourceMappingURL=SelectTrigger.d.ts.map
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import { SelectValueProps } from "@radix-ui/react-select";
3
+ export { SelectValueProps };
4
+ declare const _default: React.NamedExoticComponent<SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
5
+ export default _default;
6
+ //# sourceMappingURL=SelectValue.d.ts.map
@@ -0,0 +1,7 @@
1
+ export { default as Select, type SelectProps } from "./Select";
2
+ export { default as SelectItem, type SelectItemProps } from "./SelectItem";
3
+ export { default as SelectIcon, type SelectIconProps } from "./SelectIcon";
4
+ export { default as SelectValue, type SelectValueProps } from "./SelectValue";
5
+ export { default as SelectTrigger, type SelectTriggerProps } from "./SelectTrigger";
6
+ export { default as SelectContent, type SelectContentProps } from "./SelectContent";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -1,8 +1,10 @@
1
1
  import React, { ComponentProps } from "react";
2
+ import { HighlightProps } from "../Highlight";
2
3
  export interface TruncateProps extends ComponentProps<"span"> {
3
4
  text?: string;
4
5
  middle?: boolean;
5
6
  separator?: string;
7
+ highlight?: Omit<HighlightProps, "textToHighlight">;
6
8
  }
7
9
  declare const _default: React.NamedExoticComponent<Omit<TruncateProps, "ref"> & React.RefAttributes<HTMLSpanElement>>;
8
10
  export default _default;
@@ -15,6 +15,7 @@ export * from "./ListItem";
15
15
  export * from "./Modal";
16
16
  export * from "./Odometer";
17
17
  export * from "./ScrollArea";
18
+ export * from "./Select";
18
19
  export * from "./SvgSprite";
19
20
  export * from "./Switch";
20
21
  export * from "./Tabs";
@@ -1,4 +1,4 @@
1
- import type { AvatarProps, ButtonProps, CheckboxProps, DialogProps, DrawerProps, FooterProps, HeaderProps, HighlightProps, IconProps, IconButtonProps, ListProps, ListItemProps, ModalProps, OdometerProps, ScrollAreaProps, SwitchProps, TabsProps, TabsContentProps, TabsListProps, TabsTriggerProps, TagProps, TextAreaProps, TextFieldProps, ToastProps, TooltipProps, TruncateProps, TruncateListProps, ViewProps, ViewDrawerProps, ViewModalProps } from "../components";
1
+ import type { AvatarProps, ButtonProps, CheckboxProps, DialogProps, DrawerProps, FooterProps, HeaderProps, HighlightProps, IconProps, IconButtonProps, ListProps, ListItemProps, ModalProps, OdometerProps, ScrollAreaProps, SelectProps, SelectItemProps, SelectIconProps, SelectValueProps, SelectContentProps, SelectTriggerProps, SwitchProps, TabsProps, TabsContentProps, TabsListProps, TabsTriggerProps, TagProps, TextAreaProps, TextFieldProps, ToastProps, TooltipProps, TruncateProps, TruncateListProps, ViewProps, ViewDrawerProps, ViewModalProps } from "../components";
2
2
  export interface ComponentsProps {
3
3
  avatar?: Pick<AvatarProps, "size" | "radius" | "cursorPointer" | "delayMs">;
4
4
  button?: Pick<ButtonProps, "variant" | "color" | "size" | "radius">;
@@ -7,7 +7,7 @@ export interface ComponentsProps {
7
7
  drawer?: DrawerProps;
8
8
  footer?: FooterProps;
9
9
  header?: Pick<HeaderProps, "alignCenter" | "before" | "after">;
10
- highlight?: HighlightProps;
10
+ highlight?: Omit<HighlightProps, "textToHighlight">;
11
11
  icon?: Omit<IconProps, "name">;
12
12
  iconButton?: Pick<IconButtonProps, "variant" | "size" | "radius">;
13
13
  list?: ListProps;
@@ -15,6 +15,12 @@ export interface ComponentsProps {
15
15
  modal?: ModalProps;
16
16
  odometer?: Pick<OdometerProps, "auto" | "format" | "duration">;
17
17
  scrollArea?: ScrollAreaProps;
18
+ select?: SelectProps;
19
+ selectItem?: SelectItemProps;
20
+ selectIcon?: SelectIconProps;
21
+ selectValue?: SelectValueProps;
22
+ selectContent?: SelectContentProps;
23
+ selectTrigger?: SelectTriggerProps;
18
24
  switch?: SwitchProps;
19
25
  tabs?: TabsProps;
20
26
  tabsContent?: TabsContentProps;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "addon-ui",
3
3
  "type": "module",
4
- "version": "0.7.1",
4
+ "version": "0.9.0",
5
5
  "description": "A comprehensive React UI component library designed exclusively for the AddonBone browser extension framework with customizable theming and consistent design patterns",
6
6
  "keywords": [
7
7
  "react",
@@ -22,7 +22,11 @@
22
22
  "license": "MIT",
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "https://github.com/addon-stack/addon-ui"
25
+ "url": "git+https://github.com/addon-stack/addon-ui.git"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true
26
30
  },
27
31
  "bugs": {
28
32
  "url": "https://github.com/addon-stack/addon-ui/issues"
@@ -80,6 +84,7 @@
80
84
  "@types/react-highlight-words": "^0.20.0",
81
85
  "autosize": "^6.0.1",
82
86
  "classnames": "^2.5.1",
87
+ "debounce": "^2.2.0",
83
88
  "odometer": "^0.4.8",
84
89
  "radix-ui": "^1.1.3",
85
90
  "react-highlight-words": "^0.21.0",
@@ -91,7 +96,7 @@
91
96
  "@commitlint/cli": "^20.0.0",
92
97
  "@commitlint/config-conventional": "^20.0.0",
93
98
  "@eslint/js": "^9.21.0",
94
- "@release-it/conventional-changelog": "^10.0.1",
99
+ "@release-it/conventional-changelog": "^10.0.4",
95
100
  "@rsbuild/plugin-sass": "^1.4.0",
96
101
  "@storybook/react": "^9.1.3",
97
102
  "@types/chrome": "^0.1.12",
@@ -100,7 +105,7 @@
100
105
  "@types/node": "^22.13.10",
101
106
  "@types/react": "^19.0.10",
102
107
  "@types/react-dom": "^19.0.4",
103
- "adnbn": "^0.4.2",
108
+ "adnbn": "^0.5.4",
104
109
  "depcheck": "^1.4.7",
105
110
  "eslint": "^9.21.0",
106
111
  "eslint-plugin-react-hooks": "^5.1.0",
@@ -111,7 +116,7 @@
111
116
  "prettier": "^3.5.3",
112
117
  "react": "^19.1.0",
113
118
  "react-dom": "^19.1.0",
114
- "release-it": "^19.0.5",
119
+ "release-it": "^19.2.3",
115
120
  "rspack-plugin-virtual-module": "^1.0.0",
116
121
  "storybook": "^9.1.3",
117
122
  "storybook-react-rsbuild": "^2.1.0",
@@ -28,6 +28,7 @@ $root: accordion;
28
28
  background-color: var(--accordion-content-bg-color);
29
29
  will-change: background-color;
30
30
  transition: background-color var(--accordion-speed-bg, var(--speed-color));
31
+ overflow: hidden;
31
32
 
32
33
  &[data-state="open"] {
33
34
  animation: slideDown var(--accordion-speed-animation, var(--speed-sm)) ease-in-out;
@@ -5,7 +5,7 @@ $root: checkbox;
5
5
  align-items: center;
6
6
  justify-content: center;
7
7
  cursor: pointer;
8
- padding: 2px;
8
+ padding: var(--checkbox-padding, 2px);
9
9
  width: var(--checkbox-size, 18px);
10
10
  height: var(--checkbox-size, 18px);
11
11
  border-radius: var(--checkbox-border-radius, 4px);
@@ -1,4 +1,4 @@
1
- import React, {FC, memo} from "react";
1
+ import React, {FC, memo, useMemo} from "react";
2
2
  import classnames from "classnames";
3
3
  import Highlighter, {HighlighterProps} from "react-highlight-words";
4
4
 
@@ -8,26 +8,42 @@ import {HighlightColor} from "./types";
8
8
 
9
9
  import styles from "./highlight.module.scss";
10
10
 
11
- export interface HighlightProps extends HighlighterProps {
11
+ export interface HighlightProps extends Omit<HighlighterProps, "searchWords"> {
12
12
  color?: HighlightColor;
13
+ searchWords?: string | RegExp | (string | RegExp)[];
13
14
  }
14
15
 
15
16
  const Highlight: FC<HighlightProps> = props => {
16
- const {color, activeClassName, highlightClassName, ...other} = {
17
+ const {color, className, activeClassName, highlightClassName, searchWords, textToHighlight, ...other} = {
17
18
  ...useComponentProps("highlight"),
18
19
  ...props,
19
20
  };
20
21
 
22
+ const search = useMemo(() => {
23
+ if (!searchWords) {
24
+ return [];
25
+ }
26
+
27
+ if (Array.isArray(searchWords)) {
28
+ return searchWords;
29
+ }
30
+
31
+ return [searchWords];
32
+ }, [searchWords]);
33
+
21
34
  return (
22
35
  <Highlighter
23
- highlightClassName={classnames(
36
+ className={classnames(
24
37
  styles["highlight"],
25
38
  {
26
39
  [styles[`highlight--${color}-color`]]: color,
27
40
  },
28
- highlightClassName
41
+ className
29
42
  )}
30
- activeClassName={classnames(styles["highlight--active"], activeClassName)}
43
+ highlightClassName={classnames(styles["highlight-mark"], highlightClassName)}
44
+ activeClassName={classnames(styles["highlight-mark--active"], activeClassName)}
45
+ searchWords={search}
46
+ textToHighlight={textToHighlight}
31
47
  {...other}
32
48
  />
33
49
  );
@@ -1,51 +1,55 @@
1
1
  $root: highlight;
2
2
 
3
3
  .#{$root} {
4
- font-size: var(--highlight-font-size, inherit);
5
- font-weight: var(--highlight-font-weight, inherit);
6
- font-family: var(--highlight-font-family, inherit), sans-serif;
7
- line-height: var(--highlight-line-height, var(--line-height, 1rem));
8
- color: var(--highlight-color, #fff);
9
- background-color: var(--highlight-bg-color, #ffd60a);
10
- padding: var(--highlight-y-padding, 2px) var(--highlight-x-padding, 3px);
11
- margin: 0 calc(-1 * var(--highlight-x-padding, 3px));
12
- border-radius: var(--highlight-border-radius, 2px);
13
- transition:
14
- color var(--highlight-speed-color, var(--speed-color)),
15
- background-color var(--highlight-speed-bg, var(--speed-color));
16
-
17
- &--primary-color {
18
- color: var(--highlight-primary-color, #fff);
19
- background-color: var(--primary-color);
20
- }
4
+ display: inline;
5
+
6
+ &-mark {
7
+ font-size: var(--highlight-font-size, inherit);
8
+ font-weight: var(--highlight-font-weight, inherit);
9
+ font-family: var(--highlight-font-family, inherit), sans-serif;
10
+ line-height: var(--highlight-line-height, var(--line-height, 1rem));
11
+ color: var(--highlight-color, #fff);
12
+ background-color: var(--highlight-bg-color, #ffd60a);
13
+ padding: var(--highlight-y-padding, 2px) var(--highlight-x-padding, 3px);
14
+ margin: 0 calc(-1 * var(--highlight-x-padding, 3px));
15
+ border-radius: var(--highlight-border-radius, 2px);
16
+ transition:
17
+ color var(--highlight-speed-color, var(--speed-color)),
18
+ background-color var(--highlight-speed-bg, var(--speed-color));
19
+
20
+ .#{$root}--primary-color & {
21
+ color: var(--highlight-primary-color, #fff);
22
+ background-color: var(--primary-color);
23
+ }
21
24
 
22
- &--secondary-color {
23
- color: var(--highlight-secondary-color, #fff);
24
- background-color: var(--secondary-color);
25
- }
25
+ .#{$root}--secondary-color & {
26
+ color: var(--highlight-secondary-color, #fff);
27
+ background-color: var(--secondary-color);
28
+ }
26
29
 
27
- &--accent-color {
28
- color: var(--highlight-accent-color, #fff);
29
- background-color: var(--accent-color);
30
- }
30
+ .#{$root}--accent-color & {
31
+ color: var(--highlight-accent-color, #fff);
32
+ background-color: var(--accent-color);
33
+ }
31
34
 
32
- &--active {
33
- color: var(--highlight-active-color, var(--highlight-color, #fff));
34
- background-color: var(--highlight-active-bg-color, #ff801f);
35
+ &--active {
36
+ color: var(--highlight-active-color, var(--highlight-color, #fff));
37
+ background-color: var(--highlight-active-bg-color, #ff801f);
35
38
 
36
- &.#{$root}--primary-color {
37
- color: var(--highlight-active-primary-color, var(--highlight-primary-color, #fff));
38
- background-color: color-mix(in srgb, black 40%, var(--primary-color));
39
- }
39
+ .#{$root}--primary-color & {
40
+ color: var(--highlight-active-primary-color, var(--highlight-primary-color, #fff));
41
+ background-color: color-mix(in srgb, black 40%, var(--primary-color));
42
+ }
40
43
 
41
- &.#{$root}--secondary-color {
42
- color: var(--highlight-active-secondary-color, var(--highlight-secondary-color, #fff));
43
- background-color: color-mix(in srgb, black 40%, var(--secondary-color));
44
- }
44
+ .#{$root}--secondary-color & {
45
+ color: var(--highlight-active-secondary-color, var(--highlight-secondary-color, #fff));
46
+ background-color: color-mix(in srgb, black 40%, var(--secondary-color));
47
+ }
45
48
 
46
- &.#{$root}--accent-color {
47
- color: var(--highlight-active-accent-color, var(--highlight-accent-color, #fff));
48
- background-color: color-mix(in srgb, black 40%, var(--accent-color));
49
+ .#{$root}--accent-color & {
50
+ color: var(--highlight-active-accent-color, var(--highlight-accent-color, #fff));
51
+ background-color: color-mix(in srgb, black 40%, var(--accent-color));
52
+ }
49
53
  }
50
54
  }
51
55
  }
@@ -77,6 +77,7 @@ $root: modal;
77
77
  &-children {
78
78
  display: flex;
79
79
  flex-direction: column;
80
+ overflow: hidden;
80
81
  }
81
82
 
82
83
  &-close {
@@ -0,0 +1,19 @@
1
+ import React, {FC, memo} from "react";
2
+
3
+ import {useLocale} from "adnbn/locale/react";
4
+
5
+ import {Root, SelectProps} from "@radix-ui/react-select";
6
+
7
+ import {useComponentProps} from "../../providers";
8
+
9
+ export {SelectProps};
10
+
11
+ const Select: FC<SelectProps> = props => {
12
+ const {...other} = {...useComponentProps("select"), ...props};
13
+
14
+ const {dir} = useLocale();
15
+
16
+ return <Root dir={dir} {...other} />;
17
+ };
18
+
19
+ export default memo(Select);
@@ -0,0 +1,84 @@
1
+ import React, {forwardRef, ForwardRefRenderFunction, memo} from "react";
2
+
3
+ import classnames from "classnames";
4
+
5
+ import {
6
+ Arrow,
7
+ Content,
8
+ Portal,
9
+ SelectContentProps as SelectContentRadixProps,
10
+ SelectPortalProps,
11
+ SelectScrollDownButton,
12
+ SelectScrollUpButton,
13
+ SelectViewportProps,
14
+ Viewport,
15
+ } from "@radix-ui/react-select";
16
+
17
+ import {useComponentProps} from "../../providers";
18
+
19
+ import styles from "./select.module.scss";
20
+
21
+ export interface SelectContentProps extends SelectContentRadixProps, SelectPortalProps {
22
+ arrow?: boolean;
23
+ arrowWidth?: number;
24
+ arrowHeight?: number;
25
+ fullWidth?: boolean;
26
+ viewportProps?: SelectViewportProps;
27
+ scrollUpButton?: React.ReactNode;
28
+ scrollDownButton?: React.ReactNode;
29
+ }
30
+
31
+ const SelectContent: ForwardRefRenderFunction<HTMLDivElement, SelectContentProps> = (props, ref) => {
32
+ const {
33
+ arrow,
34
+ arrowWidth,
35
+ arrowHeight,
36
+ fullWidth,
37
+ container,
38
+ viewportProps,
39
+ scrollUpButton,
40
+ scrollDownButton,
41
+ className,
42
+ children,
43
+ ...other
44
+ } = {...useComponentProps("selectContent"), ...props};
45
+
46
+ return (
47
+ <Portal container={container}>
48
+ <Content
49
+ ref={ref}
50
+ className={classnames(
51
+ styles["select__content"],
52
+ {
53
+ [styles["select__content--full-width"]]: fullWidth,
54
+ },
55
+ className
56
+ )}
57
+ {...other}
58
+ >
59
+ {scrollUpButton && (
60
+ <SelectScrollUpButton className={classnames(styles["select__scroll-button"])}>
61
+ {scrollUpButton}
62
+ </SelectScrollUpButton>
63
+ )}
64
+
65
+ <Viewport
66
+ {...viewportProps}
67
+ className={classnames(styles["select__viewport"], viewportProps?.className)}
68
+ >
69
+ {children}
70
+ </Viewport>
71
+
72
+ {scrollDownButton && (
73
+ <SelectScrollDownButton className={classnames(styles["select__scroll-button"])}>
74
+ {scrollDownButton}
75
+ </SelectScrollDownButton>
76
+ )}
77
+
78
+ {arrow && <Arrow width={arrowWidth} height={arrowHeight} />}
79
+ </Content>
80
+ </Portal>
81
+ );
82
+ };
83
+
84
+ export default memo(forwardRef(SelectContent));
@@ -0,0 +1,19 @@
1
+ import React, {forwardRef, ForwardRefRenderFunction, memo} from "react";
2
+
3
+ import classnames from "classnames";
4
+
5
+ import {SelectIconProps, Icon} from "@radix-ui/react-select";
6
+
7
+ import {useComponentProps} from "../../providers";
8
+
9
+ import styles from "./select.module.scss";
10
+
11
+ export {SelectIconProps};
12
+
13
+ const SelectIcon: ForwardRefRenderFunction<HTMLSpanElement, SelectIconProps> = (props, ref) => {
14
+ const {className, ...other} = {...useComponentProps("selectIcon"), ...props};
15
+
16
+ return <Icon ref={ref} className={classnames(styles["select__icon"], className)} {...other} />;
17
+ };
18
+
19
+ export default memo(forwardRef(SelectIcon));
@@ -0,0 +1,41 @@
1
+ import React, {forwardRef, ForwardRefRenderFunction, memo} from "react";
2
+
3
+ import classnames from "classnames";
4
+
5
+ import {Item, ItemIndicator, ItemText, SelectItemProps as SelectItemRadixProps} from "@radix-ui/react-select";
6
+
7
+ import {useComponentProps} from "../../providers";
8
+
9
+ import styles from "./select.module.scss";
10
+
11
+ export interface SelectItemProps extends SelectItemRadixProps {
12
+ indicator?: React.ReactNode;
13
+ indicatorClassname?: string;
14
+ }
15
+
16
+ const SelectItem: ForwardRefRenderFunction<HTMLDivElement, SelectItemProps> = (props, ref) => {
17
+ const {indicator, textValue, className, indicatorClassname, children, ...other} = {
18
+ ...useComponentProps("selectItem"),
19
+ ...props,
20
+ };
21
+
22
+ return (
23
+ <Item ref={ref} className={classnames(styles["select__item"], className)} textValue={textValue} {...other}>
24
+ {children && children}
25
+
26
+ {!children && (
27
+ <>
28
+ {indicator && (
29
+ <ItemIndicator className={classnames(styles["select__item__indicator"], indicatorClassname)}>
30
+ {indicator}
31
+ </ItemIndicator>
32
+ )}
33
+
34
+ {textValue && <ItemText className={classnames(styles["select__item__text"])}>{textValue}</ItemText>}
35
+ </>
36
+ )}
37
+ </Item>
38
+ );
39
+ };
40
+
41
+ export default memo(forwardRef(SelectItem));
@@ -0,0 +1,71 @@
1
+ import React, {forwardRef, ForwardRefRenderFunction, memo} from "react";
2
+
3
+ import classnames from "classnames";
4
+
5
+ import {
6
+ Icon,
7
+ SelectIconProps,
8
+ SelectTriggerProps as SelectTriggerRadixProps,
9
+ SelectValueProps,
10
+ Trigger,
11
+ Value,
12
+ } from "@radix-ui/react-select";
13
+
14
+ import {useComponentProps} from "../../providers";
15
+
16
+ import styles from "./select.module.scss";
17
+
18
+ export interface SelectTriggerProps extends SelectTriggerRadixProps {
19
+ center?: boolean;
20
+ fullWidth?: boolean;
21
+ ellipsis?: boolean;
22
+ icon?: React.ReactNode;
23
+ placeholder?: React.ReactNode;
24
+ valueProps?: SelectValueProps;
25
+ iconProps?: SelectIconProps;
26
+ }
27
+
28
+ const SelectTrigger: ForwardRefRenderFunction<HTMLButtonElement, SelectTriggerProps> = (props, ref) => {
29
+ const {
30
+ center,
31
+ ellipsis = true,
32
+ icon,
33
+ placeholder,
34
+ valueProps,
35
+ iconProps,
36
+ className,
37
+ children,
38
+ ...other
39
+ } = {...useComponentProps("selectTrigger"), ...props};
40
+
41
+ return (
42
+ <Trigger
43
+ ref={ref}
44
+ className={classnames(
45
+ styles["select__trigger"],
46
+ {
47
+ [styles["select__trigger--center"]]: center,
48
+ [styles["select__trigger--ellipsis"]]: ellipsis,
49
+ },
50
+ className
51
+ )}
52
+ {...other}
53
+ >
54
+ {children && children}
55
+
56
+ {!children && (
57
+ <>
58
+ <Value placeholder={placeholder} {...valueProps} className={classnames(styles["select__value"])} />
59
+
60
+ {icon && (
61
+ <Icon className={classnames(styles["select__icon"], iconProps?.className)} {...iconProps}>
62
+ {icon}
63
+ </Icon>
64
+ )}
65
+ </>
66
+ )}
67
+ </Trigger>
68
+ );
69
+ };
70
+
71
+ export default memo(forwardRef(SelectTrigger));
@@ -0,0 +1,19 @@
1
+ import React, {forwardRef, ForwardRefRenderFunction, memo} from "react";
2
+
3
+ import classnames from "classnames";
4
+
5
+ import {SelectValueProps, Value} from "@radix-ui/react-select";
6
+
7
+ import {useComponentProps} from "../../providers";
8
+
9
+ import styles from "./select.module.scss";
10
+
11
+ export {SelectValueProps};
12
+
13
+ const SelectValue: ForwardRefRenderFunction<HTMLSpanElement, SelectValueProps> = (props, ref) => {
14
+ const {className, ...other} = {...useComponentProps("selectValue"), ...props};
15
+
16
+ return <Value ref={ref} className={classnames(styles["select__value"], className)} {...other} />;
17
+ };
18
+
19
+ export default memo(forwardRef(SelectValue));
@@ -0,0 +1,6 @@
1
+ export {default as Select, type SelectProps} from "./Select";
2
+ export {default as SelectItem, type SelectItemProps} from "./SelectItem";
3
+ export {default as SelectIcon, type SelectIconProps} from "./SelectIcon";
4
+ export {default as SelectValue, type SelectValueProps} from "./SelectValue";
5
+ export {default as SelectTrigger, type SelectTriggerProps} from "./SelectTrigger";
6
+ export {default as SelectContent, type SelectContentProps} from "./SelectContent";
@@ -0,0 +1,150 @@
1
+ @use "../../styles/mixins" as theme;
2
+
3
+ $root: select;
4
+
5
+ .#{$root} {
6
+ &__trigger {
7
+ all: unset;
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: space-between;
11
+ cursor: pointer;
12
+ box-sizing: border-box;
13
+ color: var(--select-trigger-color);
14
+ background-color: var(--select-trigger-bg-color, var(--bg-secondary-color));
15
+ box-shadow: var(--select-trigger-shadow-offset) var(--select-trigger-shadow-color);
16
+
17
+ border-width: var(--select-trigger-border-width, 1px);
18
+ border-style: var(--select-trigger-border-style, solid);
19
+ border-color: var(--select-trigger-border-color, var(--border-color));
20
+ border-radius: var(--select-trigger-border-radius, 10px);
21
+
22
+ font-size: var(--select-trigger-font-size, var(--select-font-size, 14px));
23
+ font-weight: var(--select-trigger-font-weight, 500);
24
+ padding: var(--select-trigger-padding, 8px 12px);
25
+ height: var(--select-trigger-height);
26
+ gap: var(--select-trigger-gap, 5px);
27
+
28
+ &--center {
29
+ justify-content: center;
30
+ }
31
+
32
+ &--ellipsis > span {
33
+ overflow: hidden;
34
+ white-space: nowrap;
35
+ text-overflow: ellipsis;
36
+ }
37
+
38
+ &:hover {
39
+ box-shadow: var(--select-trigger-shadow-offset-hover) var(--select-trigger-shadow-color);
40
+ border-width: var(--select-trigger-border-width-hover, var(--select-trigger-border-width, 1px));
41
+ border-color: var(--select-trigger-border-color-hover, var(--select-trigger-border-color, var(--border-color)));
42
+ }
43
+
44
+ &[data-disabled] {
45
+ color: var(--text-disabled-color);
46
+ }
47
+
48
+ &[data-placeholder] {
49
+ color: var(--text-disabled-color);
50
+ }
51
+
52
+ @include theme.rtl {
53
+ flex-direction: row-reverse;
54
+ }
55
+ }
56
+
57
+ &__icon {
58
+ flex-shrink: 0;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ }
63
+
64
+ &__content {
65
+ overflow: hidden;
66
+ background-color: var(--select-content-bg-color, var(--bg-primary-color));
67
+ border-radius: var(--select-content-border-radius, 8px);
68
+ box-shadow: var(--select-content-shadow-offset, 0 0 5px 0) var(--select-content-shadow-color, rgba(0, 0, 0, 0.25));
69
+ height: var(--select-content-height);
70
+ max-height: var(--select-content-max-height, var(--radix-select-content-available-height));
71
+
72
+ &--full-width {
73
+ width: var(--select-content-full-width, var(--radix-select-trigger-width));
74
+ }
75
+ }
76
+
77
+ &__viewport {
78
+ padding: var(--select-viewport-padding, 5px 0);
79
+ }
80
+
81
+ &__item {
82
+ display: flex;
83
+ align-items: center;
84
+ position: relative;
85
+ overflow: hidden;
86
+ white-space: nowrap;
87
+ font-size: var(--select-item-font-size, var(--select-font-size, 14px));
88
+ font-weight: var(--select-item-font-weight, 400);
89
+ color: var(--select-item-color, var(--text-primary-color));
90
+ background-color: var(--select-content-bg-color, --bg-primary-color);
91
+ border-radius: var(--select-item-border-radius);
92
+ height: var(--select-item-height);
93
+ padding: var(--select-item-padding, 10px);
94
+ user-select: none;
95
+
96
+ &__text {
97
+ overflow: hidden;
98
+ white-space: nowrap;
99
+ text-overflow: ellipsis;
100
+
101
+ @include theme.rtl {
102
+ text-align: end;
103
+ }
104
+ }
105
+
106
+ &__indicator {
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ }
111
+
112
+ &[data-state="checked"] {
113
+ pointer-events: none;
114
+ font-weight: var(--select-item-checked-font-weight, 600);
115
+ color: var(--select-item-checked-color);
116
+ background-color: var(--select-item-checked-bg-color, var(--bg-secondary-color));
117
+ }
118
+
119
+ &[data-disabled] {
120
+ pointer-events: none;
121
+ opacity: var(--select-item-disabled-opacity, 0.7);
122
+ color: var(--select-item-disabled-color, var(--text-disabled-color));
123
+ background-color: var(--select-item-disabled-bg-color);
124
+ }
125
+
126
+ &[data-highlighted] {
127
+ outline: none;
128
+ color: var(--select-item-highlighted-color, var(--text-primary-color));
129
+ background-color: var(--select-item-highlighted-bg-color, var(--bg-secondary-color));
130
+ }
131
+
132
+ @include theme.rtl {
133
+ flex-direction: row-reverse;
134
+ }
135
+ }
136
+
137
+ &__scroll-button {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ height: var(--select-scroll-btn-height, 25px);
142
+ color: var(--select-scroll-btn-color, var(--text-primary-color));
143
+ background-color: var(--select-scroll-btn-bg-color, var(--select-content-bg-color, var(--bg-primary-color)));
144
+ cursor: var(--select-scroll-btn-cursor, default);
145
+
146
+ &:hover {
147
+ background-color: var(--select-scroll-btn-hover-bg-color, var(--bg-secondary-color));
148
+ }
149
+ }
150
+ }
@@ -19,7 +19,10 @@ $border-width: 1px;
19
19
  position: relative;
20
20
  display: flex;
21
21
  justify-content: space-between;
22
- transition: border-bottom-color var(--tabs-speed-border-color, var(--speed-color));
22
+ background-color: var(--tabs-list-bg-color);
23
+ transition:
24
+ border-bottom-color var(--tabs-speed-border-color, var(--speed-color)),
25
+ background-color var(--tabs-speed-bg-list-color, var(--speed-color));
23
26
 
24
27
  @include theme.rtl() {
25
28
  & {
@@ -59,8 +59,8 @@ const TextArea = forwardRef<TextAreaActions, TextAreaProps>((props, ref) => {
59
59
 
60
60
  const handleChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>(
61
61
  event => {
62
- onChange?.(event);
63
62
  setValue(event.currentTarget.value);
63
+ onChange?.(event);
64
64
  },
65
65
  [onChange]
66
66
  );
@@ -91,8 +91,8 @@ const TextField = forwardRef<TextFieldActions, TextFieldProps>((props, ref) => {
91
91
 
92
92
  const handleChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
93
93
  event => {
94
- onChange?.(event);
95
94
  setValue(event.currentTarget.value);
95
+ onChange?.(event);
96
96
  },
97
97
  [onChange]
98
98
  );
@@ -5,19 +5,24 @@ import React, {
5
5
  memo,
6
6
  useImperativeHandle,
7
7
  useLayoutEffect,
8
+ useMemo,
8
9
  useRef,
9
10
  useState,
10
11
  } from "react";
12
+ import debounce from "debounce";
11
13
  import classnames from "classnames";
12
14
 
13
15
  import {useComponentProps} from "../../providers";
14
16
 
17
+ import {Highlight, HighlightProps} from "../Highlight";
18
+
15
19
  import styles from "./truncate.module.scss";
16
20
 
17
21
  export interface TruncateProps extends ComponentProps<"span"> {
18
22
  text?: string;
19
23
  middle?: boolean;
20
24
  separator?: string;
25
+ highlight?: Omit<HighlightProps, "textToHighlight">;
21
26
  }
22
27
 
23
28
  const trimMiddle = (el: HTMLElement, text: string, separator: string) => {
@@ -50,52 +55,48 @@ const trimMiddle = (el: HTMLElement, text: string, separator: string) => {
50
55
  };
51
56
 
52
57
  const Truncate: ForwardRefRenderFunction<HTMLSpanElement, TruncateProps> = (props, ref) => {
53
- const {text = "", middle, separator = "...", className, ...other} = {...useComponentProps("truncate"), ...props};
58
+ const {
59
+ text = "",
60
+ middle,
61
+ separator = "...",
62
+ className,
63
+ highlight,
64
+ ...other
65
+ } = {...useComponentProps("truncate"), ...props};
54
66
 
55
67
  const innerRef = useRef<HTMLSpanElement | null>(null);
56
68
  const [displayedText, setDisplayedText] = useState(text);
57
69
 
70
+ const finalText = useMemo(() => {
71
+ return middle ? displayedText : text;
72
+ }, [displayedText, text, middle]);
73
+
58
74
  useImperativeHandle(ref, () => innerRef.current!, []);
59
75
 
60
76
  useLayoutEffect(() => {
61
77
  const el = innerRef.current;
62
78
  if (!el || !middle) return;
63
79
 
64
- let animationFrameId: number;
65
80
  let observer: ResizeObserver | null = null;
66
81
 
67
- const measureAndTrim = () => {
68
- animationFrameId = requestAnimationFrame(() => {
69
- const newText = trimMiddle(el, text, separator);
70
- if (newText !== displayedText) {
71
- setDisplayedText(newText);
72
- }
73
- });
74
- };
82
+ const measureAndTrim = debounce(() => {
83
+ setDisplayedText(trimMiddle(el, text, separator));
84
+ }, 150);
75
85
 
76
86
  measureAndTrim();
77
87
 
78
- if ("ResizeObserver" in window) {
79
- observer = new ResizeObserver(() => {
80
- cancelAnimationFrame(animationFrameId);
81
- measureAndTrim();
82
- });
83
- observer.observe(el);
84
- }
88
+ observer = new ResizeObserver(() => measureAndTrim());
85
89
 
86
- window.addEventListener("resize", measureAndTrim);
90
+ observer.observe(el);
87
91
 
88
92
  return () => {
89
- window.removeEventListener("resize", measureAndTrim);
93
+ measureAndTrim.clear();
90
94
  observer?.disconnect();
91
- cancelAnimationFrame(animationFrameId);
92
95
  };
93
- // eslint-disable-next-line react-hooks/exhaustive-deps
94
96
  }, [text, separator, middle]);
95
97
 
96
98
  return (
97
99
  <span
98
- ref={innerRef}
99
100
  className={classnames(
100
101
  styles["truncate"],
101
102
  {
@@ -105,7 +106,9 @@ const Truncate: ForwardRefRenderFunction<HTMLSpanElement, TruncateProps> = (prop
105
106
  )}
106
107
  {...other}
107
108
  >
108
- {middle ? displayedText : text}
109
+ <span ref={innerRef} className={styles["truncate__hidden"]} />
110
+
111
+ {highlight ? <Highlight {...highlight} textToHighlight={finalText} /> : finalText}
109
112
  </span>
110
113
  );
111
114
  };
@@ -1,6 +1,7 @@
1
1
  @use "../../styles/mixins" as theme;
2
2
 
3
3
  .truncate {
4
+ position: relative;
4
5
  display: block;
5
6
  width: 100%;
6
7
  white-space: nowrap;
@@ -8,6 +9,15 @@
8
9
  text-overflow: ellipsis;
9
10
  transition: color var(--truncate-speed-color, var(--speed-color));
10
11
 
12
+ &__hidden {
13
+ opacity: 0;
14
+ position: absolute;
15
+ width: 100%;
16
+ white-space: nowrap;
17
+ overflow: hidden;
18
+ text-overflow: ellipsis;
19
+ }
20
+
11
21
  &--middle {
12
22
  text-overflow: clip;
13
23
 
@@ -15,6 +15,7 @@ export * from "./ListItem";
15
15
  export * from "./Modal";
16
16
  export * from "./Odometer";
17
17
  export * from "./ScrollArea";
18
+ export * from "./Select";
18
19
  export * from "./SvgSprite";
19
20
  export * from "./Switch";
20
21
  export * from "./Tabs";
@@ -14,6 +14,12 @@ import type {
14
14
  ModalProps,
15
15
  OdometerProps,
16
16
  ScrollAreaProps,
17
+ SelectProps,
18
+ SelectItemProps,
19
+ SelectIconProps,
20
+ SelectValueProps,
21
+ SelectContentProps,
22
+ SelectTriggerProps,
17
23
  SwitchProps,
18
24
  TabsProps,
19
25
  TabsContentProps,
@@ -39,7 +45,7 @@ export interface ComponentsProps {
39
45
  drawer?: DrawerProps;
40
46
  footer?: FooterProps;
41
47
  header?: Pick<HeaderProps, "alignCenter" | "before" | "after">;
42
- highlight?: HighlightProps;
48
+ highlight?: Omit<HighlightProps, "textToHighlight">;
43
49
  icon?: Omit<IconProps, "name">;
44
50
  iconButton?: Pick<IconButtonProps, "variant" | "size" | "radius">;
45
51
  list?: ListProps;
@@ -47,6 +53,12 @@ export interface ComponentsProps {
47
53
  modal?: ModalProps;
48
54
  odometer?: Pick<OdometerProps, "auto" | "format" | "duration">;
49
55
  scrollArea?: ScrollAreaProps;
56
+ select?: SelectProps;
57
+ selectItem?: SelectItemProps;
58
+ selectIcon?: SelectIconProps;
59
+ selectValue?: SelectValueProps;
60
+ selectContent?: SelectContentProps;
61
+ selectTrigger?: SelectTriggerProps;
50
62
  switch?: SwitchProps;
51
63
  tabs?: TabsProps;
52
64
  tabsContent?: TabsContentProps;