adnbn-ui 0.0.1 → 0.1.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 (146) hide show
  1. package/.prettierignore +3 -0
  2. package/.prettierrc +28 -0
  3. package/.storybook/main.ts +22 -0
  4. package/.storybook/preview.tsx +100 -0
  5. package/.storybook/styles/custom.scss +59 -0
  6. package/.storybook/styles/preview.css +58 -0
  7. package/.storybook/vitest.setup.ts +9 -0
  8. package/README.md +1057 -0
  9. package/eslint.config.js +39 -0
  10. package/package.json +95 -4
  11. package/src/components/Avatar/Avatar.stories.tsx +118 -0
  12. package/src/components/Avatar/Avatar.tsx +65 -0
  13. package/src/components/Avatar/avatar.module.scss +77 -0
  14. package/src/components/Avatar/index.ts +2 -0
  15. package/src/components/BaseButton/BaseButton.tsx +36 -0
  16. package/src/components/BaseButton/base-button.module.scss +24 -0
  17. package/src/components/BaseButton/index.ts +2 -0
  18. package/src/components/Button/Button.stories.tsx +148 -0
  19. package/src/components/Button/Button.tsx +73 -0
  20. package/src/components/Button/button.module.scss +140 -0
  21. package/src/components/Button/index.ts +2 -0
  22. package/src/components/Checkbox/Checkbox.stories.tsx +180 -0
  23. package/src/components/Checkbox/Checkbox.tsx +71 -0
  24. package/src/components/Checkbox/checkbox.module.scss +82 -0
  25. package/src/components/Checkbox/index.ts +2 -0
  26. package/src/components/Dialog/Dialog.tsx +125 -0
  27. package/src/components/Dialog/dialog.module.scss +55 -0
  28. package/src/components/Dialog/index.ts +2 -0
  29. package/src/components/Drawer/Drawer.stories.tsx +89 -0
  30. package/src/components/Drawer/Drawer.tsx +57 -0
  31. package/src/components/Drawer/drawer.module.scss +170 -0
  32. package/src/components/Drawer/index.ts +2 -0
  33. package/src/components/Footer/Footer.stories.tsx +118 -0
  34. package/src/components/Footer/Footer.tsx +58 -0
  35. package/src/components/Footer/footer.module.scss +44 -0
  36. package/src/components/Footer/index.ts +2 -0
  37. package/src/components/Header/Header.stories.tsx +49 -0
  38. package/src/components/Header/Header.tsx +73 -0
  39. package/src/components/Header/header.module.scss +56 -0
  40. package/src/components/Header/index.ts +2 -0
  41. package/src/components/Highlight/Highlight.stories.tsx +83 -0
  42. package/src/components/Highlight/Highlight.tsx +40 -0
  43. package/src/components/Highlight/highlight.module.scss +47 -0
  44. package/src/components/Highlight/index.ts +2 -0
  45. package/src/components/Icon/Icon.tsx +46 -0
  46. package/src/components/Icon/icon.module.scss +17 -0
  47. package/src/components/Icon/index.ts +2 -0
  48. package/src/components/IconButton/IconButton.stories.tsx +179 -0
  49. package/src/components/IconButton/IconButton.tsx +65 -0
  50. package/src/components/IconButton/icon-button.module.scss +86 -0
  51. package/src/components/IconButton/index.ts +2 -0
  52. package/src/components/Layout/Layout.stories.tsx +88 -0
  53. package/src/components/Layout/Provider.tsx +47 -0
  54. package/src/components/Layout/context.ts +24 -0
  55. package/src/components/Layout/index.ts +2 -0
  56. package/src/components/Layout/layout.module.scss +17 -0
  57. package/src/components/List/List.stories.tsx +81 -0
  58. package/src/components/List/List.tsx +24 -0
  59. package/src/components/List/index.ts +2 -0
  60. package/src/components/List/list.module.scss +8 -0
  61. package/src/components/ListItem/ListItem.tsx +75 -0
  62. package/src/components/ListItem/index.ts +2 -0
  63. package/src/components/ListItem/list-item.module.scss +36 -0
  64. package/src/components/Modal/Modal.stories.tsx +95 -0
  65. package/src/components/Modal/Modal.tsx +94 -0
  66. package/src/components/Modal/index.ts +2 -0
  67. package/src/components/Modal/modal.module.scss +97 -0
  68. package/src/components/Odometer/Odometer.stories.tsx +66 -0
  69. package/src/components/Odometer/Odometer.tsx +45 -0
  70. package/src/components/Odometer/hooks/useOdometer.tsx +24 -0
  71. package/src/components/Odometer/index.ts +3 -0
  72. package/src/components/Odometer/odometer.module.scss +81 -0
  73. package/src/components/Odometer/odometr.d.ts +9 -0
  74. package/src/components/ScrollArea/ScrollArea.stories.tsx +58 -0
  75. package/src/components/ScrollArea/ScrollArea.tsx +63 -0
  76. package/src/components/ScrollArea/index.ts +2 -0
  77. package/src/components/ScrollArea/scroll-area.module.scss +54 -0
  78. package/src/components/SvgSprite/SvgSprite.tsx +21 -0
  79. package/src/components/SvgSprite/index.ts +2 -0
  80. package/src/components/Switch/Switch.stories.tsx +25 -0
  81. package/src/components/Switch/Switch.tsx +23 -0
  82. package/src/components/Switch/index.ts +2 -0
  83. package/src/components/Switch/switch.module.scss +65 -0
  84. package/src/components/Tag/Tag.stories.tsx +157 -0
  85. package/src/components/Tag/Tag.tsx +71 -0
  86. package/src/components/Tag/index.ts +2 -0
  87. package/src/components/Tag/tag.module.scss +118 -0
  88. package/src/components/TextArea/TextArea.stories.tsx +145 -0
  89. package/src/components/TextArea/TextArea.tsx +143 -0
  90. package/src/components/TextArea/index.ts +2 -0
  91. package/src/components/TextArea/text-area.module.scss +88 -0
  92. package/src/components/TextField/TextField.stories.tsx +177 -0
  93. package/src/components/TextField/TextField.tsx +162 -0
  94. package/src/components/TextField/index.ts +2 -0
  95. package/src/components/TextField/text-field.module.scss +129 -0
  96. package/src/components/Toast/Toast.stories.tsx +209 -0
  97. package/src/components/Toast/Toast.tsx +142 -0
  98. package/src/components/Toast/index.ts +2 -0
  99. package/src/components/Toast/toast.module.scss +267 -0
  100. package/src/components/Tooltip/Tooltip.stories.tsx +80 -0
  101. package/src/components/Tooltip/Tooltip.tsx +79 -0
  102. package/src/components/Tooltip/index.ts +2 -0
  103. package/src/components/Tooltip/tooltip.module.scss +93 -0
  104. package/src/components/View/View.stories.tsx +47 -0
  105. package/src/components/View/View.tsx +68 -0
  106. package/src/components/View/index.ts +2 -0
  107. package/src/components/View/view.module.scss +38 -0
  108. package/src/components/ViewDrawer/ViewDrawer.stories.tsx +75 -0
  109. package/src/components/ViewDrawer/ViewDrawer.tsx +24 -0
  110. package/src/components/ViewDrawer/index.ts +2 -0
  111. package/src/components/ViewModal/ViewModal.stories.tsx +68 -0
  112. package/src/components/ViewModal/ViewModal.tsx +24 -0
  113. package/src/components/ViewModal/index.ts +2 -0
  114. package/src/components/index.ts +29 -0
  115. package/src/components/types.ts +65 -0
  116. package/src/config/default.ts +3 -0
  117. package/src/config/index.ts +26 -0
  118. package/src/declaration.d.ts +8 -0
  119. package/src/index.ts +3 -0
  120. package/src/plugin/builder/ConfigBuilder.ts +32 -0
  121. package/src/plugin/builder/StyleBuilder.ts +34 -0
  122. package/src/plugin/builder/virtual.config.ts +7 -0
  123. package/src/plugin/finder/ConfigFinder.ts +26 -0
  124. package/src/plugin/finder/Finder.ts +76 -0
  125. package/src/plugin/finder/StyleFinder.ts +23 -0
  126. package/src/plugin/index.ts +70 -0
  127. package/src/plugin/types.ts +8 -0
  128. package/src/providers/UIProvider.tsx +26 -0
  129. package/src/providers/icons/IconsProvider.tsx +34 -0
  130. package/src/providers/icons/context.ts +22 -0
  131. package/src/providers/icons/index.ts +3 -0
  132. package/src/providers/index.ts +3 -0
  133. package/src/providers/theme/ThemeProvider.tsx +39 -0
  134. package/src/providers/theme/context.ts +30 -0
  135. package/src/providers/theme/index.ts +2 -0
  136. package/src/providers/theme/styles/default.scss +95 -0
  137. package/src/providers/theme/styles/reset.css +111 -0
  138. package/src/styles/mixins.scss +23 -0
  139. package/src/types/theme.ts +4 -0
  140. package/src/utils/index.ts +2 -0
  141. package/src/utils/react.ts +21 -0
  142. package/src/utils/utils.ts +12 -0
  143. package/tsconfig.json +18 -0
  144. package/vite.config.ts +11 -0
  145. package/vitest.workspace.ts +19 -0
  146. package/components/Button/index.ts +0 -0
@@ -0,0 +1,39 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ import globals from "globals";
5
+
6
+ import reactHooks from "eslint-plugin-react-hooks";
7
+ import reactRefresh from "eslint-plugin-react-refresh";
8
+
9
+ export default tseslint.config(
10
+ {
11
+ ignores: [
12
+ "dist",
13
+ "addon",
14
+ ".adnbn",
15
+ "build",
16
+ "node_modules",
17
+ "coverage",
18
+ ".storybook",
19
+ "public",
20
+ "*.config.js",
21
+ ],
22
+ },
23
+ {
24
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
25
+ files: ["**/*.{ts,tsx}"],
26
+ languageOptions: {
27
+ ecmaVersion: 2020,
28
+ globals: globals.browser,
29
+ },
30
+ plugins: {
31
+ "react-hooks": reactHooks,
32
+ "react-refresh": reactRefresh,
33
+ },
34
+ rules: {
35
+ ...reactHooks.configs.recommended.rules,
36
+ "react-refresh/only-export-components": "off",
37
+ },
38
+ }
39
+ );
package/package.json CHANGED
@@ -1,7 +1,98 @@
1
1
  {
2
2
  "name": "adnbn-ui",
3
- "version": "0.0.1",
4
- "description": "UI Libs for AddonBone",
5
- "author": "AddonBone",
6
- "license": "MIT"
3
+ "version": "0.1.0",
4
+ "description": "A comprehensive React UI component library designed exclusively for the AddonBone browser extension framework with customizable theming and consistent design patterns",
5
+ "keywords": [
6
+ "react",
7
+ "ui",
8
+ "components",
9
+ "adnbn",
10
+ "addonbone",
11
+ "browser-extension",
12
+ "theme",
13
+ "customizable",
14
+ "typescript",
15
+ "design-system"
16
+ ],
17
+ "author": "Addon Bone",
18
+ "license": "MIT",
19
+ "type": "module",
20
+ "exports": {
21
+ ".": "./src/index.ts",
22
+ "./config": "./src/config/index.ts",
23
+ "./plugin": "./src/plugin/index.ts",
24
+ "./theme": "./src/styles/mixins.scss"
25
+ },
26
+ "sideEffects": [
27
+ "*.css",
28
+ "*.scss"
29
+ ],
30
+ "scripts": {
31
+ "dev": "vite",
32
+ "build": "tsc -b && vite build",
33
+ "lint": "eslint .",
34
+ "preview": "vite preview",
35
+ "storybook": "storybook dev -p 6006",
36
+ "build-storybook": "storybook build",
37
+ "format": "prettier --write ."
38
+ },
39
+ "dependencies": {
40
+ "adnbn": "^0.0.54",
41
+ "autosize": "^6.0.1",
42
+ "classnames": "^2.5.1",
43
+ "odometer": "^0.4.8",
44
+ "radix-ui": "^1.1.3",
45
+ "sass": "^1.85.1",
46
+ "react-highlight-words": "^0.21.0",
47
+ "ts-deepmerge": "^7.0.3"
48
+ },
49
+ "devDependencies": {
50
+ "@eslint/js": "^9.21.0",
51
+ "@storybook/addon-essentials": "^8.6.4",
52
+ "@storybook/react": "^8.6.4",
53
+ "@storybook/react-vite": "^8.6.4",
54
+ "@storybook/test": "^8.6.4",
55
+ "@svgr/webpack": "^8.1.0",
56
+ "@types/autosize": "^4.0.3",
57
+ "@types/node": "^22.13.10",
58
+ "@types/react": "^19.0.10",
59
+ "@types/react-dom": "^19.0.4",
60
+ "@types/react-highlight-words": "^0.20.0",
61
+ "@vitejs/plugin-react": "^4.3.4",
62
+ "@vitest/browser": "^3.0.8",
63
+ "@vitest/coverage-v8": "^3.0.8",
64
+ "eslint": "^9.21.0",
65
+ "eslint-plugin-react-hooks": "^5.1.0",
66
+ "eslint-plugin-react-refresh": "^0.4.19",
67
+ "globals": "^15.15.0",
68
+ "playwright": "^1.51.0",
69
+ "rspack-plugin-virtual-module": "^1.0.0",
70
+ "storybook": "^8.6.4",
71
+ "react": "^19.1.0",
72
+ "react-dom": "^19.1.0",
73
+ "typescript": "~5.7.2",
74
+ "typescript-eslint": "^8.24.1",
75
+ "vite": "^6.2.1",
76
+ "vitest": "^3.0.8",
77
+ "prettier": "^3.5.3"
78
+ },
79
+ "peerDependencies": {
80
+ "@types/react": "*",
81
+ "@types/react-dom": "*",
82
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
83
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
84
+ },
85
+ "peerDependenciesMeta": {
86
+ "@types/react": {
87
+ "optional": true
88
+ },
89
+ "@types/react-dom": {
90
+ "optional": true
91
+ }
92
+ },
93
+ "eslintConfig": {
94
+ "extends": [
95
+ "plugin:storybook/recommended"
96
+ ]
97
+ }
7
98
  }
@@ -0,0 +1,118 @@
1
+ import {Meta, StoryObj} from "@storybook/react";
2
+
3
+ import {capitalizeFirstLetter, hideInTable} from "../../utils";
4
+
5
+ import AvatarComponent, {AvatarRadius, AvatarSize} from "./Avatar";
6
+
7
+ const sizes: (AvatarSize | "default")[] = [AvatarSize.Small, "default", AvatarSize.Medium, AvatarSize.Large];
8
+ const radius: (AvatarRadius | "default")[] = [AvatarRadius.Small, AvatarRadius.Medium, AvatarRadius.Large, "default"];
9
+
10
+ const meta: Meta<typeof AvatarComponent> = {
11
+ title: "Components/Avatar",
12
+ component: AvatarComponent,
13
+ tags: ["autodocs"],
14
+ argTypes: {
15
+ size: {
16
+ options: sizes,
17
+ control: {type: "select"},
18
+ },
19
+ radius: {
20
+ options: radius,
21
+ control: {type: "select"},
22
+ },
23
+ fallbackClassName: hideInTable,
24
+ imageClassName: hideInTable,
25
+ children: hideInTable,
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+
31
+ export const Avatar: StoryObj<typeof AvatarComponent> = {
32
+ args: {
33
+ src: "https://images.unsplash.com/photo-1492633423870-43d1cd2775eb?&w=128&h=128&dpr=2&q=80",
34
+ fallback: "CT",
35
+ },
36
+ };
37
+
38
+ export const AvatarRadiusGrid = () => {
39
+ return (
40
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
41
+ {radius.map(radius => (
42
+ <div key={radius} className="item-card">
43
+ <span className="item-card__title">{capitalizeFirstLetter(radius)}</span>
44
+ <AvatarComponent
45
+ src="https://images.unsplash.com/photo-1492633423870-43d1cd2775eb?&w=128&h=128&dpr=2&q=80"
46
+ radius={radius !== "default" ? radius : undefined}
47
+ fallback="CT"
48
+ />
49
+ </div>
50
+ ))}
51
+ </div>
52
+ );
53
+ };
54
+
55
+ export const Size = () => {
56
+ return (
57
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
58
+ {sizes.map(size => (
59
+ <div key={size} className="item-card">
60
+ <span className="item-card__title">{capitalizeFirstLetter(size)}</span>
61
+ <AvatarComponent
62
+ src="https://images.unsplash.com/photo-1492633423870-43d1cd2775eb?&w=128&h=128&dpr=2&q=80"
63
+ size={size !== "default" ? size : undefined}
64
+ fallback="CT"
65
+ />
66
+ </div>
67
+ ))}
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export const SizeWithSVG = () => {
73
+ return (
74
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
75
+ {sizes.map(size => (
76
+ <div key={size} className="item-card">
77
+ <span className="item-card__title">{capitalizeFirstLetter(size)}</span>
78
+ <AvatarComponent
79
+ src="https://freesvg.org/img/Female-Avatar-5.png"
80
+ size={size !== "default" ? size : undefined}
81
+ fallback="CT"
82
+ />
83
+ </div>
84
+ ))}
85
+ </div>
86
+ );
87
+ };
88
+
89
+ export const SizeWithFallback = () => {
90
+ return (
91
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
92
+ {sizes.map(size => (
93
+ <div key={size} className="item-card">
94
+ <span className="item-card__title">{capitalizeFirstLetter(size)}</span>
95
+ <AvatarComponent src="" size={size !== "default" ? size : undefined} fallback="CT" />
96
+ </div>
97
+ ))}
98
+ </div>
99
+ );
100
+ };
101
+
102
+ export const SizeRadius = () => {
103
+ return (
104
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
105
+ {sizes.map(size =>
106
+ radius.map(radius => (
107
+ <div key={`${radius}-${size}`} className="item-card">
108
+ <AvatarComponent
109
+ src="https://images.unsplash.com/photo-1492633423870-43d1cd2775eb?&w=128&h=128&dpr=2&q=80"
110
+ radius={radius !== "default" ? radius : undefined}
111
+ size={size !== "default" ? size : undefined}
112
+ />
113
+ </div>
114
+ ))
115
+ )}
116
+ </div>
117
+ );
118
+ };
@@ -0,0 +1,65 @@
1
+ import React, {FC, memo, ReactNode} from "react";
2
+ import classnames from "classnames";
3
+ import {AvatarFallbackProps, AvatarImageProps, Fallback, Image, Root} from "@radix-ui/react-avatar";
4
+
5
+ import {useComponentProps} from "../../providers";
6
+
7
+ import styles from "./avatar.module.scss";
8
+
9
+ export enum AvatarSize {
10
+ Small = "small",
11
+ Medium = "medium",
12
+ Large = "large",
13
+ }
14
+
15
+ export enum AvatarRadius {
16
+ Small = "small",
17
+ Medium = "medium",
18
+ Large = "large",
19
+ }
20
+
21
+ export interface AvatarProps extends AvatarImageProps, Pick<AvatarFallbackProps, "delayMs"> {
22
+ imageClassName?: string;
23
+ size?: AvatarSize;
24
+ radius?: AvatarRadius;
25
+ fallback?: ReactNode;
26
+ fallbackClassName?: string;
27
+ cursorPointer?: boolean;
28
+ }
29
+
30
+ const Avatar: FC<AvatarProps> = props => {
31
+ const {
32
+ size,
33
+ radius,
34
+ fallback,
35
+ fallbackClassName,
36
+ delayMs = 600,
37
+ cursorPointer,
38
+ imageClassName,
39
+ className,
40
+ ...other
41
+ } = {...useComponentProps("avatar"), ...props};
42
+
43
+ return (
44
+ <Root
45
+ className={classnames(
46
+ styles["avatar"],
47
+ {
48
+ [styles[`avatar--${size}-size`]]: size,
49
+ [styles[`avatar--${radius}-radius`]]: radius,
50
+ [styles[`avatar--cursor-pointer`]]: cursorPointer,
51
+ },
52
+ className
53
+ )}
54
+ >
55
+ <Image className={classnames(styles["avatar-image"], imageClassName)} {...other} />
56
+ {fallback && (
57
+ <Fallback className={classnames(styles["avatar-fallback"], fallbackClassName)} delayMs={delayMs}>
58
+ {fallback}
59
+ </Fallback>
60
+ )}
61
+ </Root>
62
+ );
63
+ };
64
+
65
+ export default memo(Avatar);
@@ -0,0 +1,77 @@
1
+ $root: avatar;
2
+
3
+ .#{$root} {
4
+ box-sizing: border-box;
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ vertical-align: middle;
9
+ overflow: hidden;
10
+ cursor: default;
11
+ width: var(--avatar-size, 45px);
12
+ height: var(--avatar-size, 45px);
13
+ background-color: var(--avatar-bg-color, transparent);
14
+ border-radius: var(--avatar-border-radius, 100%);
15
+ border-width: var(--avatar-border-width);
16
+ border-color: var(--avatar-border-color);
17
+ border-style: var(--avatar-border-style);
18
+ font-size: var(--avatar-font-size, 18px);
19
+ font-weight: var(--avatar-font-weight, 500);
20
+
21
+ &--cursor-pointer {
22
+ cursor: pointer;
23
+ }
24
+
25
+ // Size
26
+ &--small-size {
27
+ width: var(--avatar-size-sm, 35px);
28
+ height: var(--avatar-size-sm, 35px);
29
+ font-size: var(--avatar-font-size-sm, 14px);
30
+ }
31
+
32
+ &--medium-size {
33
+ width: var(--avatar-size-md, 55px);
34
+ height: var(--avatar-size-md, 55px);
35
+ font-size: var(--avatar-font-size-md, 22px);
36
+ }
37
+
38
+ &--large-size {
39
+ width: var(--avatar-size-lg, 65px);
40
+ height: var(--avatar-size-lg, 65px);
41
+ font-size: var(--avatar-font-size-lg, 26px);
42
+ }
43
+
44
+ // Radius
45
+ &--small-radius {
46
+ border-radius: var(--avatar-border-radius-sm, 20%);
47
+ }
48
+
49
+ &--medium-radius {
50
+ border-radius: var(--avatar-border-radius-md, 30%);
51
+ }
52
+
53
+ &--large-radius {
54
+ border-radius: var(--avatar-border-radius-lg, 40%);
55
+ }
56
+
57
+ &-image {
58
+ width: 100%;
59
+ height: 100%;
60
+ object-fit: cover;
61
+ border-radius: inherit;
62
+ }
63
+
64
+ &-fallback {
65
+ width: 100%;
66
+ height: 100%;
67
+ cursor: inherit;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ color: var(--avatar-fallback-color);
72
+ background-color: var(--avatar-fallback-bg-color, var(--avatar-bg-color));
73
+ border-width: var(--avatar-fallback-border-width, var(--avatar-border-width));
74
+ border-color: var(--avatar-fallback-border-color, var(--avatar-border-color));
75
+ border-style: var(--avatar-fallback-border-style, var(--avatar-border-style));
76
+ }
77
+ }
@@ -0,0 +1,2 @@
1
+ export {default as Avatar, AvatarSize, AvatarRadius} from "./Avatar";
2
+ export type {AvatarProps} from "./Avatar";
@@ -0,0 +1,36 @@
1
+ import React, {ComponentProps, FC, memo, ReactNode} from "react";
2
+ import classnames from "classnames";
3
+
4
+ import {cloneOrCreateElement} from "../../utils";
5
+
6
+ import styles from "./base-button.module.scss";
7
+
8
+ export interface BaseButtonProps extends ComponentProps<"button"> {
9
+ after?: ReactNode;
10
+ before?: ReactNode;
11
+ afterClassName?: string;
12
+ beforeClassName?: string;
13
+ childrenClassName?: string;
14
+ }
15
+
16
+ const BaseButton: FC<BaseButtonProps> = props => {
17
+ const {after, before, children, className, afterClassName, beforeClassName, childrenClassName, ...other} = props;
18
+
19
+ return (
20
+ <button className={classnames(styles["base-button"], className)} {...other}>
21
+ {cloneOrCreateElement(before, {
22
+ className: classnames(styles["base-button__before"], beforeClassName),
23
+ })}
24
+
25
+ {cloneOrCreateElement(children, {
26
+ className: classnames(styles["base-button__children"], childrenClassName),
27
+ })}
28
+
29
+ {cloneOrCreateElement(after, {
30
+ className: classnames(styles["base-button__after"], afterClassName),
31
+ })}
32
+ </button>
33
+ );
34
+ };
35
+
36
+ export default memo(BaseButton);
@@ -0,0 +1,24 @@
1
+ .base-button {
2
+ box-sizing: border-box;
3
+ border: none;
4
+ cursor: pointer;
5
+ position: relative;
6
+ display: inline-flex;
7
+ flex-direction: row;
8
+ align-items: center;
9
+ vertical-align: middle;
10
+ justify-content: center;
11
+ gap: var(--base-button-gap, 8px);
12
+ will-change: background, color, transform;
13
+ transition:
14
+ background var(--transition-speed-sm),
15
+ color var(--transition-speed-sm),
16
+ transform var(--transition-speed-sm);
17
+
18
+ &__after,
19
+ &__before,
20
+ &__children {
21
+ position: relative;
22
+ display: inline;
23
+ }
24
+ }
@@ -0,0 +1,2 @@
1
+ export {default as BaseButton} from "./BaseButton";
2
+ export type {BaseButtonProps} from "./BaseButton";
@@ -0,0 +1,148 @@
1
+ import {Meta, StoryObj} from "@storybook/react";
2
+
3
+ import {capitalizeFirstLetter, hideInTable} from "../../utils";
4
+
5
+ import ButtonComponent, {ButtonColor, ButtonRadius, ButtonSize, ButtonVariant} from "./Button";
6
+
7
+ const variants: ButtonVariant[] = [ButtonVariant.Contained, ButtonVariant.Outlined, ButtonVariant.Text];
8
+ const colors: (ButtonColor | "default")[] = ["default", ButtonColor.Primary, ButtonColor.Secondary, ButtonColor.Accent];
9
+ const sizes: (ButtonSize | "default")[] = [ButtonSize.Small, "default", ButtonSize.Medium, ButtonSize.Large];
10
+ const radius: (ButtonRadius | "default")[] = [
11
+ ButtonRadius.Small,
12
+ "default",
13
+ ButtonRadius.Medium,
14
+ ButtonRadius.Large,
15
+ ButtonRadius.Full,
16
+ ];
17
+
18
+ const meta: Meta<typeof ButtonComponent> = {
19
+ title: "Components/Button",
20
+ component: ButtonComponent,
21
+ tags: ["autodocs"],
22
+ argTypes: {
23
+ variant: {
24
+ options: variants,
25
+ control: {type: "select"},
26
+ },
27
+ color: {
28
+ options: colors,
29
+ control: {type: "select"},
30
+ },
31
+ size: {
32
+ options: sizes,
33
+ control: {type: "select"},
34
+ },
35
+ radius: {
36
+ options: radius,
37
+ control: {type: "select"},
38
+ },
39
+
40
+ after: hideInTable,
41
+ before: hideInTable,
42
+ afterClassName: hideInTable,
43
+ beforeClassName: hideInTable,
44
+ childrenClassName: hideInTable,
45
+ },
46
+ };
47
+
48
+ export default meta;
49
+
50
+ export const Button: StoryObj<typeof ButtonComponent> = {
51
+ args: {
52
+ children: "Button",
53
+ },
54
+ };
55
+
56
+ export const VariantColor = () => {
57
+ return (
58
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
59
+ {variants.map(variant =>
60
+ colors.map(color => (
61
+ <div key={`${variant}-${color}`}>
62
+ <ButtonComponent variant={variant} color={color !== "default" ? color : undefined}>
63
+ {capitalizeFirstLetter(color)}
64
+ </ButtonComponent>
65
+ </div>
66
+ ))
67
+ )}
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export const VariantColorDisabled = () => {
73
+ return (
74
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
75
+ {variants.map(variant =>
76
+ colors.map(color => (
77
+ <div key={`${variant}-${color}`}>
78
+ <ButtonComponent disabled variant={variant} color={color !== "default" ? color : undefined}>
79
+ {capitalizeFirstLetter(color)}
80
+ </ButtonComponent>
81
+ </div>
82
+ ))
83
+ )}
84
+ </div>
85
+ );
86
+ };
87
+
88
+ export const VariantSize = () => {
89
+ return (
90
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(4, auto)"}}>
91
+ {variants.map(variant =>
92
+ sizes.map(size => (
93
+ <div key={`${variant}-${size}`}>
94
+ <ButtonComponent variant={variant} size={size !== "default" ? size : undefined}>
95
+ {capitalizeFirstLetter(size)}
96
+ </ButtonComponent>
97
+ </div>
98
+ ))
99
+ )}
100
+ </div>
101
+ );
102
+ };
103
+
104
+ export const VariantRadius = () => {
105
+ return (
106
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(5, auto)"}}>
107
+ {variants.map(variant =>
108
+ radius.map(radius => (
109
+ <div key={`${variant}-${radius}`}>
110
+ <ButtonComponent variant={variant} radius={radius !== "default" ? radius : undefined}>
111
+ {capitalizeFirstLetter(radius)}
112
+ </ButtonComponent>
113
+ </div>
114
+ ))
115
+ )}
116
+ </div>
117
+ );
118
+ };
119
+
120
+ export const SizeRadius = () => {
121
+ return (
122
+ <div className="grid-wrapper" style={{gridTemplateColumns: "repeat(5, auto)"}}>
123
+ <span className="item-card__title"> Radius \ Size </span>
124
+ {sizes.map(size => (
125
+ <span key={size} className="item-card__title">
126
+ {capitalizeFirstLetter(size)} size
127
+ </span>
128
+ ))}
129
+ {radius.map(radius => (
130
+ <>
131
+ <span key={radius} className="item-card__title">
132
+ {capitalizeFirstLetter(radius)} radius
133
+ </span>
134
+ {sizes.map(size => (
135
+ <div key={`${size}-${radius}`} className="item-card">
136
+ <ButtonComponent
137
+ size={size !== "default" ? size : undefined}
138
+ radius={radius !== "default" ? radius : undefined}
139
+ >
140
+ Button
141
+ </ButtonComponent>
142
+ </div>
143
+ ))}
144
+ </>
145
+ ))}
146
+ </div>
147
+ );
148
+ };
@@ -0,0 +1,73 @@
1
+ import React, {FC, memo} from "react";
2
+ import classnames from "classnames";
3
+ import {BaseButton, BaseButtonProps} from "../BaseButton";
4
+
5
+ import {useComponentProps} from "../../providers";
6
+
7
+ import styles from "./button.module.scss";
8
+
9
+ export enum ButtonVariant {
10
+ Contained = "contained",
11
+ Outlined = "outlined",
12
+ Text = "text",
13
+ }
14
+
15
+ export enum ButtonColor {
16
+ Primary = "primary",
17
+ Secondary = "secondary",
18
+ Accent = "accent",
19
+ }
20
+
21
+ export enum ButtonSize {
22
+ Small = "small",
23
+ Medium = "medium",
24
+ Large = "large",
25
+ }
26
+
27
+ export enum ButtonRadius {
28
+ Small = "small",
29
+ Medium = "medium",
30
+ Large = "large",
31
+ Full = "full",
32
+ }
33
+
34
+ export interface ButtonProps extends BaseButtonProps {
35
+ variant?: ButtonVariant;
36
+ color?: ButtonColor;
37
+ size?: ButtonSize;
38
+ radius?: ButtonRadius;
39
+ }
40
+
41
+ const Button: FC<ButtonProps> = props => {
42
+ const {
43
+ variant = ButtonVariant.Contained,
44
+ color,
45
+ size,
46
+ radius,
47
+ childrenClassName,
48
+ className,
49
+ children,
50
+ ...other
51
+ } = {...useComponentProps("button"), ...props};
52
+
53
+ return (
54
+ <BaseButton
55
+ {...other}
56
+ className={classnames(
57
+ styles["button"],
58
+ {
59
+ [styles[`button--${variant}`]]: variant,
60
+ [styles[`button--${radius}-radius`]]: radius,
61
+ [styles[`button--${color}-color`]]: color,
62
+ [styles[`button--${size}-size`]]: size,
63
+ },
64
+ className
65
+ )}
66
+ childrenClassName={classnames(styles["button__text"], childrenClassName)}
67
+ >
68
+ {children}
69
+ </BaseButton>
70
+ );
71
+ };
72
+
73
+ export default memo(Button);