@mohammadsalman/storybook-custom-ui 1.0.1 → 1.0.3
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.
- package/README.md +90 -133
- package/dist/index.cjs.js +1 -0
- package/dist/index.esm.js +1 -0
- package/dist/vite.svg +1 -0
- package/package.json +48 -40
- package/src/components/Button/Button.stories.tsx +0 -109
- package/src/components/Button/Button.tsx +0 -144
- package/src/components/Button/index.ts +0 -1
- package/src/components/Card/Card.tsx +0 -119
- package/src/components/Card/card.stories.tsx +0 -127
- package/src/components/Card/index.ts +0 -1
- package/src/components/index.ts +0 -1
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useFormInput.stories.tsx +0 -126
- package/src/hooks/useFormInput.ts +0 -114
- package/src/index.ts +0 -11
- package/src/theme/ThemeProvider.tsx +0 -34
- package/src/theme/defaultTheme.ts +0 -182
- package/src/theme/index.ts +0 -3
- package/src/theme/types.ts +0 -106
- package/src/utils/styles.ts +0 -15
package/README.md
CHANGED
|
@@ -1,189 +1,146 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mohammadsalman/storybook-custom-ui
|
|
2
2
|
|
|
3
|
-
A minimal React TypeScript component library with Storybook, featuring
|
|
3
|
+
A minimal React TypeScript component library with Storybook, featuring reusable UI components built with React 19 and TypeScript.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- ✅ Custom `useFormInput` hook for form input management
|
|
9
|
-
- ✅ TypeScript support with full type safety
|
|
10
|
-
- ✅ Theming system that can be adapted to any project
|
|
11
|
-
- ✅ Storybook with interactive controls
|
|
12
|
-
- ✅ Zero external UI library dependencies
|
|
13
|
-
|
|
14
|
-
## Getting Started
|
|
15
|
-
|
|
16
|
-
### Installation
|
|
7
|
+
### Install via npm
|
|
17
8
|
|
|
18
9
|
```bash
|
|
19
|
-
npm install
|
|
10
|
+
npm install @mohammadsalman/storybook-custom-ui
|
|
20
11
|
```
|
|
21
12
|
|
|
22
|
-
###
|
|
13
|
+
### Install via yarn
|
|
23
14
|
|
|
24
15
|
```bash
|
|
25
|
-
|
|
16
|
+
yarn add @mohammadsalman/storybook-custom-ui
|
|
26
17
|
```
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
### Building Storybook
|
|
19
|
+
### Install via pnpm
|
|
31
20
|
|
|
32
21
|
```bash
|
|
33
|
-
|
|
22
|
+
pnpm add @mohammadsalman/storybook-custom-ui
|
|
34
23
|
```
|
|
35
24
|
|
|
36
|
-
##
|
|
25
|
+
## Peer Dependencies
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
This package requires React 19.2.0 or higher. Make sure you have the following installed in your project:
|
|
39
28
|
|
|
40
|
-
|
|
29
|
+
```bash
|
|
30
|
+
npm install react@^19.2.0 react-dom@^19.2.0
|
|
31
|
+
```
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
## Usage
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
- **Typography**: Font families, sizes, weights, and line heights for all text variants
|
|
46
|
-
- **Spacing**: Consistent spacing unit (default: 8px)
|
|
47
|
-
- **Shadows**: Elevation shadows (0-24)
|
|
48
|
-
- **Shape**: Border radius values
|
|
49
|
-
- **Z-Index**: Layering system
|
|
35
|
+
### Import Components
|
|
50
36
|
|
|
51
|
-
|
|
37
|
+
```tsx
|
|
38
|
+
import { Button } from '@mohammadsalman/storybook-custom-ui';
|
|
39
|
+
```
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
### Example
|
|
54
42
|
|
|
55
43
|
```tsx
|
|
56
|
-
import
|
|
44
|
+
import React from 'react';
|
|
45
|
+
import { Button } from '@mohammadsalman/storybook-custom-ui';
|
|
57
46
|
|
|
58
47
|
function App() {
|
|
59
48
|
return (
|
|
60
|
-
<
|
|
61
|
-
<
|
|
62
|
-
|
|
49
|
+
<div>
|
|
50
|
+
<Button
|
|
51
|
+
primary={true}
|
|
52
|
+
size="large"
|
|
53
|
+
label="Click me"
|
|
54
|
+
onClick={() => console.log('Button clicked!')}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
63
57
|
);
|
|
64
58
|
}
|
|
59
|
+
|
|
60
|
+
export default App;
|
|
65
61
|
```
|
|
66
62
|
|
|
67
|
-
##
|
|
63
|
+
## Development
|
|
68
64
|
|
|
69
|
-
###
|
|
65
|
+
### Prerequisites
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
- Node.js 18+
|
|
68
|
+
- npm, yarn, or pnpm
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
import { Button } from './components';
|
|
70
|
+
### Setup
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
1. Clone the repository:
|
|
73
|
+
```bash
|
|
74
|
+
git clone https://github.com/MohSalman/creative-ui-react-ui-library.git
|
|
75
|
+
cd creative-ui-react-ui-library
|
|
79
76
|
```
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- `fullWidth`: boolean
|
|
86
|
-
- `disabled`: boolean
|
|
87
|
-
|
|
88
|
-
## Available Hooks
|
|
78
|
+
2. Install dependencies:
|
|
79
|
+
```bash
|
|
80
|
+
npm install
|
|
81
|
+
```
|
|
89
82
|
|
|
90
|
-
|
|
83
|
+
3. Start Storybook to view components:
|
|
84
|
+
```bash
|
|
85
|
+
npm run storybook
|
|
86
|
+
```
|
|
91
87
|
|
|
92
|
-
|
|
88
|
+
This will start Storybook on `http://localhost:6006`
|
|
93
89
|
|
|
94
|
-
|
|
95
|
-
import { useFormInput } from './hooks';
|
|
96
|
-
|
|
97
|
-
const MyForm = () => {
|
|
98
|
-
const emailInput = useFormInput({
|
|
99
|
-
initialValue: '',
|
|
100
|
-
validate: (value) => {
|
|
101
|
-
if (!value) return 'Email is required';
|
|
102
|
-
if (!/\S+@\S+\.\S+/.test(value)) return 'Invalid email format';
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
});
|
|
90
|
+
### Build
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
value={emailInput.value}
|
|
111
|
-
onChange={emailInput.onChange}
|
|
112
|
-
onBlur={emailInput.onBlur}
|
|
113
|
-
/>
|
|
114
|
-
{emailInput.error && <span>{emailInput.error}</span>}
|
|
115
|
-
<button onClick={emailInput.reset}>Reset</button>
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
};
|
|
92
|
+
Build the library:
|
|
93
|
+
```bash
|
|
94
|
+
npm run build
|
|
119
95
|
```
|
|
120
96
|
|
|
121
|
-
|
|
122
|
-
- `initialValue`: string (default: '')
|
|
123
|
-
- `validate`: (value: string) => string | null
|
|
124
|
-
- `onChange`: (value: string) => void (optional callback)
|
|
125
|
-
|
|
126
|
-
**Returns:**
|
|
127
|
-
- `value`: string
|
|
128
|
-
- `error`: string | null
|
|
129
|
-
- `onChange`: (e: ChangeEvent) => void
|
|
130
|
-
- `onBlur`: () => void
|
|
131
|
-
- `setValue`: (value: string) => void
|
|
132
|
-
- `setError`: (error: string | null) => void
|
|
133
|
-
- `reset`: () => void
|
|
134
|
-
- `isValid`: boolean
|
|
97
|
+
This will create the `dist/` folder with the compiled files.
|
|
135
98
|
|
|
136
|
-
|
|
99
|
+
### Preview
|
|
137
100
|
|
|
101
|
+
Preview the built application:
|
|
102
|
+
```bash
|
|
103
|
+
npm run preview
|
|
138
104
|
```
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
├── hooks/ # Custom hooks
|
|
146
|
-
│ ├── useFormInput.ts
|
|
147
|
-
│ ├── useFormInput.stories.tsx
|
|
148
|
-
│ └── index.ts
|
|
149
|
-
├── theme/ # Theming system
|
|
150
|
-
│ ├── types.ts
|
|
151
|
-
│ ├── defaultTheme.ts
|
|
152
|
-
│ ├── ThemeProvider.tsx
|
|
153
|
-
│ └── index.ts
|
|
154
|
-
└── utils/ # Utility functions
|
|
155
|
-
└── styles.ts
|
|
156
|
-
.storybook/ # Storybook configuration
|
|
157
|
-
├── main.ts
|
|
158
|
-
├── preview.tsx
|
|
159
|
-
└── global.css
|
|
105
|
+
|
|
106
|
+
### Lint
|
|
107
|
+
|
|
108
|
+
Run ESLint:
|
|
109
|
+
```bash
|
|
110
|
+
npm run lint
|
|
160
111
|
```
|
|
161
112
|
|
|
162
|
-
##
|
|
113
|
+
## Available Scripts
|
|
163
114
|
|
|
164
|
-
|
|
115
|
+
- `npm run dev` - Start Vite development server
|
|
116
|
+
- `npm run build` - Build the library for production
|
|
117
|
+
- `npm run lint` - Run ESLint
|
|
118
|
+
- `npm run preview` - Preview the built application
|
|
119
|
+
- `npm run storybook` - Start Storybook development server
|
|
120
|
+
- `npm run build-storybook` - Build Storybook for static hosting
|
|
165
121
|
|
|
166
|
-
|
|
167
|
-
2. Add your component file (e.g., `MyComponent.tsx`)
|
|
168
|
-
3. Create an `index.ts` file that exports your component
|
|
169
|
-
4. Create a `MyComponent.stories.tsx` file for Storybook
|
|
170
|
-
5. Export from `src/components/index.ts`
|
|
122
|
+
## Project Structure
|
|
171
123
|
|
|
172
|
-
|
|
124
|
+
```
|
|
125
|
+
src/
|
|
126
|
+
├── stories/ # Storybook stories and components
|
|
127
|
+
│ ├── Button/
|
|
128
|
+
│ ├── Header/
|
|
129
|
+
│ └── Page/
|
|
130
|
+
├── index.ts # Main entry point
|
|
131
|
+
└── ...
|
|
132
|
+
.storybook/ # Storybook configuration
|
|
133
|
+
dist/ # Built files (generated)
|
|
134
|
+
```
|
|
173
135
|
|
|
174
|
-
|
|
136
|
+
## License
|
|
175
137
|
|
|
176
|
-
|
|
177
|
-
2. Export from `src/hooks/index.ts`
|
|
178
|
-
3. Optionally create a `useMyHook.stories.tsx` file for Storybook
|
|
138
|
+
MIT
|
|
179
139
|
|
|
180
|
-
##
|
|
140
|
+
## Author
|
|
181
141
|
|
|
182
|
-
|
|
142
|
+
Mohammad Salman <mohammadsalman71993@gmail.com>
|
|
183
143
|
|
|
184
|
-
|
|
185
|
-
2. Create your custom theme based on your design system
|
|
186
|
-
3. Wrap your app with `ThemeProvider` using your custom theme
|
|
187
|
-
4. Import and use components and hooks as needed
|
|
144
|
+
## Repository
|
|
188
145
|
|
|
189
|
-
|
|
146
|
+
[https://github.com/MohSalman/creative-ui-react-ui-library](https://github.com/MohSalman/creative-ui-react-ui-library)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/dist/vite.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/package.json
CHANGED
|
@@ -1,56 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mohammadsalman/storybook-custom-ui",
|
|
3
|
-
"version": "1.0.1",
|
|
4
3
|
"description": "A minimal React TypeScript component library with Button component and useFormInput hook",
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
],
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"typescript",
|
|
14
|
-
"components",
|
|
15
|
-
"hooks",
|
|
16
|
-
"storybook",
|
|
17
|
-
"button",
|
|
18
|
-
"form-input"
|
|
19
|
-
],
|
|
4
|
+
|
|
5
|
+
"version": "1.0.3",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.esm.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": ["dist"],
|
|
10
|
+
|
|
11
|
+
"keywords": ["react", "typescript", "components", "storybook"],
|
|
20
12
|
"author": "Mohammad Salman <mohammadsalman71993@gmail.com>",
|
|
21
13
|
"license": "MIT",
|
|
22
14
|
"repository": {
|
|
23
15
|
"type": "git",
|
|
24
16
|
"url": "https://github.com/MohSalman/creative-ui-react-ui-library.git"
|
|
25
17
|
},
|
|
26
|
-
"
|
|
27
|
-
|
|
18
|
+
"type": "module",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "tsc -b && vite build",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"preview": "vite preview",
|
|
24
|
+
"storybook": "storybook dev -p 6006",
|
|
25
|
+
"build-storybook": "storybook build"
|
|
28
26
|
},
|
|
27
|
+
|
|
29
28
|
"peerDependencies": {
|
|
30
|
-
"react": "^
|
|
31
|
-
"react-dom": "^
|
|
29
|
+
"react": "^19.2.0",
|
|
30
|
+
"react-dom": "^19.2.0"
|
|
32
31
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
32
|
+
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"react": "^19.2.0",
|
|
35
|
+
"react-dom": "^19.2.0"
|
|
37
36
|
},
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
"devDependencies": {
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"storybook": "^
|
|
54
|
-
"
|
|
39
|
+
"@eslint/js": "^9.39.1",
|
|
40
|
+
"@types/node": "^24.10.1",
|
|
41
|
+
"@types/react": "^19.2.5",
|
|
42
|
+
"@types/react-dom": "^19.2.3",
|
|
43
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
44
|
+
"eslint": "^9.39.1",
|
|
45
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
46
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
47
|
+
"globals": "^16.5.0",
|
|
48
|
+
"typescript": "~5.9.3",
|
|
49
|
+
"typescript-eslint": "^8.46.4",
|
|
50
|
+
"vite": "^7.2.4",
|
|
51
|
+
"storybook": "^10.1.11",
|
|
52
|
+
"@storybook/react-vite": "^10.1.11",
|
|
53
|
+
"@chromatic-com/storybook": "^5.0.0",
|
|
54
|
+
"@storybook/addon-vitest": "^10.1.11",
|
|
55
|
+
"@storybook/addon-a11y": "^10.1.11",
|
|
56
|
+
"@storybook/addon-docs": "^10.1.11",
|
|
57
|
+
"@storybook/addon-onboarding": "^10.1.11",
|
|
58
|
+
"eslint-plugin-storybook": "^10.1.11",
|
|
59
|
+
"vitest": "^4.0.17",
|
|
60
|
+
"playwright": "^1.57.0",
|
|
61
|
+
"@vitest/browser-playwright": "^4.0.17",
|
|
62
|
+
"@vitest/coverage-v8": "^4.0.17"
|
|
55
63
|
}
|
|
56
64
|
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { Button } from './Button';
|
|
3
|
-
|
|
4
|
-
const meta: Meta<typeof Button> = {
|
|
5
|
-
title: 'Components/Button',
|
|
6
|
-
component: Button,
|
|
7
|
-
tags: ['autodocs'],
|
|
8
|
-
argTypes: {
|
|
9
|
-
variant: {
|
|
10
|
-
control: { type: 'select' },
|
|
11
|
-
options: ['contained', 'outlined', 'text'],
|
|
12
|
-
},
|
|
13
|
-
color: {
|
|
14
|
-
control: { type: 'select' },
|
|
15
|
-
options: ['primary', 'secondary', 'success', 'error', 'warning', 'info'],
|
|
16
|
-
},
|
|
17
|
-
size: {
|
|
18
|
-
control: { type: 'select' },
|
|
19
|
-
options: ['small', 'medium', 'large'],
|
|
20
|
-
},
|
|
21
|
-
fullWidth: {
|
|
22
|
-
control: { type: 'boolean' },
|
|
23
|
-
},
|
|
24
|
-
disabled: {
|
|
25
|
-
control: { type: 'boolean' },
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export default meta;
|
|
31
|
-
type Story = StoryObj<typeof Button>;
|
|
32
|
-
|
|
33
|
-
export const Contained: Story = {
|
|
34
|
-
args: {
|
|
35
|
-
children: 'Button',
|
|
36
|
-
variant: 'contained',
|
|
37
|
-
color: 'primary',
|
|
38
|
-
size: 'medium',
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const Outlined: Story = {
|
|
43
|
-
args: {
|
|
44
|
-
children: 'Button',
|
|
45
|
-
variant: 'outlined',
|
|
46
|
-
color: 'primary',
|
|
47
|
-
size: 'medium',
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const Text: Story = {
|
|
52
|
-
args: {
|
|
53
|
-
children: 'Button',
|
|
54
|
-
variant: 'text',
|
|
55
|
-
color: 'primary',
|
|
56
|
-
size: 'medium',
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const Colors: Story = {
|
|
61
|
-
render: () => (
|
|
62
|
-
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
|
63
|
-
<Button color="primary">Primary</Button>
|
|
64
|
-
<Button color="secondary">Secondary</Button>
|
|
65
|
-
<Button color="success">Success</Button>
|
|
66
|
-
<Button color="error">Error</Button>
|
|
67
|
-
<Button color="warning">Warning</Button>
|
|
68
|
-
<Button color="info">Info</Button>
|
|
69
|
-
</div>
|
|
70
|
-
),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const Sizes: Story = {
|
|
74
|
-
render: () => (
|
|
75
|
-
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
|
76
|
-
<Button size="small">Small</Button>
|
|
77
|
-
<Button size="medium">Medium</Button>
|
|
78
|
-
<Button size="large">Large</Button>
|
|
79
|
-
</div>
|
|
80
|
-
),
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export const Variants: Story = {
|
|
84
|
-
render: () => (
|
|
85
|
-
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
|
86
|
-
<Button variant="contained">Contained</Button>
|
|
87
|
-
<Button variant="outlined">Outlined</Button>
|
|
88
|
-
<Button variant="text">Text</Button>
|
|
89
|
-
</div>
|
|
90
|
-
),
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export const FullWidth: Story = {
|
|
94
|
-
args: {
|
|
95
|
-
children: 'Full Width Button',
|
|
96
|
-
fullWidth: true,
|
|
97
|
-
variant: 'contained',
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export const Disabled: Story = {
|
|
102
|
-
render: () => (
|
|
103
|
-
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
|
104
|
-
<Button variant="contained" disabled>Disabled</Button>
|
|
105
|
-
<Button variant="outlined" disabled>Disabled</Button>
|
|
106
|
-
<Button variant="text" disabled>Disabled</Button>
|
|
107
|
-
</div>
|
|
108
|
-
),
|
|
109
|
-
};
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useTheme } from '../../theme';
|
|
3
|
-
import { spacing } from '../../utils/styles';
|
|
4
|
-
|
|
5
|
-
export type ButtonVariant = 'contained' | 'outlined' | 'text';
|
|
6
|
-
export type ButtonColor = 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
|
|
7
|
-
export type ButtonSize = 'small' | 'medium' | 'large';
|
|
8
|
-
|
|
9
|
-
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
10
|
-
variant?: ButtonVariant;
|
|
11
|
-
color?: ButtonColor;
|
|
12
|
-
size?: ButtonSize;
|
|
13
|
-
fullWidth?: boolean;
|
|
14
|
-
disabled?: boolean;
|
|
15
|
-
children: React.ReactNode;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const Button: React.FC<ButtonProps> = ({
|
|
19
|
-
variant = 'contained',
|
|
20
|
-
color = 'primary',
|
|
21
|
-
size = 'medium',
|
|
22
|
-
fullWidth = false,
|
|
23
|
-
disabled = false,
|
|
24
|
-
children,
|
|
25
|
-
style,
|
|
26
|
-
...props
|
|
27
|
-
}) => {
|
|
28
|
-
const theme = useTheme();
|
|
29
|
-
|
|
30
|
-
const getVariantStyles = (): React.CSSProperties => {
|
|
31
|
-
const colorValue = theme.palette[color][500];
|
|
32
|
-
const textColor = variant === 'contained' ? '#ffffff' : colorValue;
|
|
33
|
-
|
|
34
|
-
switch (variant) {
|
|
35
|
-
case 'contained':
|
|
36
|
-
return {
|
|
37
|
-
backgroundColor: colorValue,
|
|
38
|
-
color: textColor,
|
|
39
|
-
border: 'none',
|
|
40
|
-
boxShadow: theme.shadows[2],
|
|
41
|
-
};
|
|
42
|
-
case 'outlined':
|
|
43
|
-
return {
|
|
44
|
-
backgroundColor: 'transparent',
|
|
45
|
-
color: colorValue,
|
|
46
|
-
border: `1px solid ${colorValue}`,
|
|
47
|
-
boxShadow: 'none',
|
|
48
|
-
};
|
|
49
|
-
case 'text':
|
|
50
|
-
return {
|
|
51
|
-
backgroundColor: 'transparent',
|
|
52
|
-
color: colorValue,
|
|
53
|
-
border: 'none',
|
|
54
|
-
boxShadow: 'none',
|
|
55
|
-
};
|
|
56
|
-
default:
|
|
57
|
-
return {};
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const getSizeStyles = (): React.CSSProperties => {
|
|
62
|
-
switch (size) {
|
|
63
|
-
case 'small':
|
|
64
|
-
return {
|
|
65
|
-
padding: spacing(theme, 0.5, 1.5),
|
|
66
|
-
fontSize: theme.typography.button.fontSize,
|
|
67
|
-
minWidth: '64px',
|
|
68
|
-
};
|
|
69
|
-
case 'medium':
|
|
70
|
-
return {
|
|
71
|
-
padding: spacing(theme, 1, 2),
|
|
72
|
-
fontSize: theme.typography.button.fontSize,
|
|
73
|
-
minWidth: '64px',
|
|
74
|
-
};
|
|
75
|
-
case 'large':
|
|
76
|
-
return {
|
|
77
|
-
padding: spacing(theme, 1.5, 3),
|
|
78
|
-
fontSize: '1rem',
|
|
79
|
-
minWidth: '64px',
|
|
80
|
-
};
|
|
81
|
-
default:
|
|
82
|
-
return {};
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const baseStyles: React.CSSProperties = {
|
|
87
|
-
fontFamily: theme.typography.button.fontFamily,
|
|
88
|
-
fontWeight: theme.typography.button.fontWeight,
|
|
89
|
-
lineHeight: theme.typography.button.lineHeight,
|
|
90
|
-
letterSpacing: theme.typography.button.letterSpacing,
|
|
91
|
-
textTransform: 'uppercase',
|
|
92
|
-
borderRadius: `${theme.shape.borderRadius}px`,
|
|
93
|
-
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
94
|
-
outline: 'none',
|
|
95
|
-
transition: 'background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
|
|
96
|
-
width: fullWidth ? '100%' : 'auto',
|
|
97
|
-
opacity: disabled ? 0.6 : 1,
|
|
98
|
-
...getVariantStyles(),
|
|
99
|
-
...getSizeStyles(),
|
|
100
|
-
...style,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const handleMouseEnter = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
104
|
-
if (!disabled && props.onMouseEnter) {
|
|
105
|
-
props.onMouseEnter(e);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const handleMouseLeave = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
110
|
-
if (!disabled && props.onMouseLeave) {
|
|
111
|
-
props.onMouseLeave(e);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<button
|
|
117
|
-
{...props}
|
|
118
|
-
disabled={disabled}
|
|
119
|
-
style={baseStyles}
|
|
120
|
-
onMouseEnter={handleMouseEnter}
|
|
121
|
-
onMouseLeave={handleMouseLeave}
|
|
122
|
-
onMouseOver={(e) => {
|
|
123
|
-
if (!disabled) {
|
|
124
|
-
e.currentTarget.style.backgroundColor =
|
|
125
|
-
variant === 'contained'
|
|
126
|
-
? theme.palette[color][700]
|
|
127
|
-
: variant === 'outlined'
|
|
128
|
-
? theme.palette.action.hover
|
|
129
|
-
: theme.palette.action.hover;
|
|
130
|
-
}
|
|
131
|
-
}}
|
|
132
|
-
onMouseOut={(e) => {
|
|
133
|
-
if (!disabled) {
|
|
134
|
-
e.currentTarget.style.backgroundColor =
|
|
135
|
-
variant === 'contained'
|
|
136
|
-
? theme.palette[color][500]
|
|
137
|
-
: 'transparent';
|
|
138
|
-
}
|
|
139
|
-
}}
|
|
140
|
-
>
|
|
141
|
-
{children}
|
|
142
|
-
</button>
|
|
143
|
-
);
|
|
144
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './Button';
|