@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.
@@ -1,119 +0,0 @@
1
- import React from 'react';
2
- import { useTheme } from '../../theme';
3
- import { spacing } from '../../utils/styles';
4
-
5
- export interface CardProps {
6
- variant?: 'outlined' | 'elevated' | 'flat';
7
-
8
- heading?: string;
9
- subheading?: string;
10
- description?: string;
11
- children?: React.ReactNode;
12
- className?: string;
13
- cardContentClass?: string;
14
- cardHeadingClass?: string;
15
- cardSubheadingClass?: string;
16
- cardDescriptionClass?: string;
17
- cardActionsClass?: string;
18
-
19
- actions?: React.ReactNode;
20
-
21
- // Styling
22
- elevation?: number; // 0-24 (for elevated variant)
23
- style?: React.CSSProperties;
24
-
25
- // Optional callbacks
26
- onClick?: () => void;
27
- }
28
-
29
- export const Card: React.FC<CardProps> = ({
30
- variant = 'outlined',
31
- heading,
32
- subheading,
33
- description,
34
- children,
35
- className,
36
- cardContentClass,
37
- cardHeadingClass,
38
- cardSubheadingClass,
39
- cardDescriptionClass,
40
- cardActionsClass,
41
- actions,
42
- elevation = 2, // 0-24 (for elevated variant)
43
- style,
44
- onClick,
45
- ...props
46
- }) => {
47
- const theme = useTheme();
48
-
49
- const getVariantStyles = (): React.CSSProperties => {
50
- switch (variant) {
51
- case 'outlined':
52
- return {
53
- border: `1px solid ${theme.palette.grey[300]}`,
54
- }
55
- case 'elevated':
56
- return {
57
- boxShadow: theme.shadows[elevation],
58
- }
59
- case 'flat':
60
- return {
61
- boxShadow: 'none',
62
- }
63
- default:
64
- return {};
65
- }; }
66
-
67
- const baseStyles: React.CSSProperties = {
68
- fontFamily: theme.typography.button.fontFamily,
69
- lineHeight: theme.typography.button.lineHeight,
70
- letterSpacing: theme.typography.button.letterSpacing,
71
- padding: spacing(theme, 2),
72
- borderRadius: `${theme.shape.borderRadius}px`,
73
- // cursor: disabled ? 'not-allowed' : 'pointer',
74
- outline: 'none',
75
- 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',
76
- // width: fullWidth ? '100%' : 'auto',
77
- // opacity: disabled ? 0.6 : 1,
78
- ...getVariantStyles(),
79
- // ...getSizeStyles(),
80
- ...style,
81
- };
82
-
83
- const cardContentStyles: React.CSSProperties = {
84
- fontSize: theme.typography.body1.fontSize,
85
- fontWeight: theme.typography.body1.fontWeight,
86
- };
87
-
88
- const cardHeadingStyles: React.CSSProperties = {
89
- fontSize: theme.typography.h6.fontSize,
90
- fontWeight: theme.typography.h6.fontWeight,
91
- };
92
- const cardSubheadingStyles: React.CSSProperties = {
93
- fontSize: theme.typography.body2.fontSize,
94
- fontWeight: theme.typography.body2.fontWeight,
95
- };
96
- const cardDescriptionStyles: React.CSSProperties = {
97
- fontSize: theme.typography.body1.fontSize,
98
- fontWeight: theme.typography.body1.fontWeight,
99
- };
100
-
101
- const cardActionsStyles: React.CSSProperties = {
102
- display: 'flex',
103
- justifyContent: 'flex-end',
104
- alignItems: 'center',
105
- };
106
-
107
- return (
108
- <div className={className} style={baseStyles} onClick={onClick} {...props}>
109
- <div className={cardContentClass} style={cardContentStyles}>
110
- <div className={cardHeadingClass} style={cardHeadingStyles}>{heading}</div>
111
- <hr/>
112
- <div className={cardSubheadingClass} style={cardSubheadingStyles}>{subheading}</div>
113
- <div className={cardDescriptionClass} style={cardDescriptionStyles}>{description}</div>
114
- </div>
115
-
116
- <div className={cardActionsClass} style={cardActionsStyles}>{actions}</div>
117
- </div>
118
- );
119
- };
@@ -1,127 +0,0 @@
1
- import { Meta, StoryObj } from "@storybook/react"; /* Card Stories */
2
- import { Card } from "./Card";
3
- import { Button } from "../Button";
4
-
5
- const meta: Meta<typeof Card> = {
6
- title: "Components/Card",
7
- component: Card,
8
- tags: ["autodocs"],
9
- argTypes: {
10
- variant: {
11
- control: { type: "select" },
12
- options: ["outlined", "elevated", "flat"],
13
- },
14
- },
15
- args: {
16
- variant: "outlined",
17
- heading: "Card Heading",
18
- subheading: "Card Subheading",
19
- description: "Card Description",
20
- children: "Card Content",
21
- },
22
- render: (args) => <Card {...args} />,
23
- };
24
-
25
- export default meta;
26
- type Story = StoryObj<typeof Card>;
27
-
28
- export const Default: Story = {
29
- args: {
30
- variant: "outlined",
31
- heading: "Card Heading",
32
- subheading: "Card Subheading",
33
- description: "Card Description",
34
- children: "Card Content",
35
- },
36
- };
37
-
38
- export const Outlined: Story = {
39
- args: {
40
- variant: "outlined",
41
- heading: "Card Heading",
42
- subheading: "Card Subheading",
43
- description: "Card Description",
44
- children: "Card Content",
45
- },
46
- };
47
-
48
- export const Elevated: Story = {
49
- args: {
50
- variant: "elevated",
51
- heading: "Card Heading",
52
- subheading: "Card Subheading",
53
- description: "Card Description",
54
- children: "Card Content",
55
- },
56
- };
57
-
58
- export const Flat: Story = {
59
- args: {
60
- variant: "flat",
61
- heading: "Card Heading",
62
- subheading: "Card Subheading",
63
- description: "Card Description",
64
- children: "Card Content",
65
- },
66
- };
67
- export const WithActions: Story = {
68
- args: {
69
- variant: "outlined",
70
- heading: "Card Heading",
71
- subheading: "Card Subheading",
72
- description: "Card Description",
73
- children: "Card Content",
74
- actions: <Button>Action</Button>,
75
- },
76
- };
77
- export const WithChildren: Story = {
78
- args: {
79
- variant: "outlined",
80
- heading: "Card Heading",
81
- subheading: "Card Subheading",
82
- description: "Card Description",
83
- children: "Card Content",
84
- },
85
- };
86
- export const WithElevation: Story = {
87
- args: {
88
- variant: "elevated",
89
- heading: "Card Heading",
90
- subheading: "Card Subheading",
91
- description: "Card Description",
92
- children: "Card Content",
93
- elevation: 2,
94
- },
95
- };
96
- export const WithActionsAndChildren: Story = {
97
- args: {
98
- variant: "outlined",
99
- heading: "Card Heading",
100
- subheading: "Card Subheading",
101
- description: "Card Description",
102
- children: "Card Content",
103
- actions: <Button>Action</Button>,
104
- },
105
- };
106
- export const WithActionsAndChildrenAndElevation: Story = {
107
- args: {
108
- variant: "elevated",
109
- heading: "Card Heading",
110
- subheading: "Card Subheading",
111
- description: "Card Description",
112
- children: "Card Content",
113
- actions: <Button>Action</Button>,
114
- elevation: 2,
115
- },
116
- };
117
- export const WithActionsAndChildrenAndElevationAndVariant: Story = {
118
- args: {
119
- variant: "elevated",
120
- heading: "Card Heading",
121
- subheading: "Card Subheading",
122
- description: "Card Description",
123
- children: "Card Content",
124
- actions: <Button>Action</Button>,
125
- elevation: 2,
126
- },
127
- };
@@ -1 +0,0 @@
1
- export * from './Card';
@@ -1 +0,0 @@
1
- export * from './Button';
@@ -1 +0,0 @@
1
- export * from './useFormInput';
@@ -1,126 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react';
2
- import { useFormInput } from './useFormInput';
3
- import { Button } from '../components/Button';
4
-
5
- // Demo component to showcase the hook
6
- const FormInputDemo = ({
7
- initialValue,
8
- validate,
9
- label
10
- }: {
11
- initialValue?: string;
12
- validate?: (value: string) => string | null;
13
- label?: string;
14
- }) => {
15
- const input = useFormInput({
16
- initialValue,
17
- validate,
18
- });
19
-
20
- return (
21
- <div style={{ maxWidth: '400px', padding: '20px' }}>
22
- <label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
23
- {label || 'Input'}
24
- </label>
25
- <input
26
- type="text"
27
- value={input.value}
28
- onChange={input.onChange}
29
- onBlur={input.onBlur}
30
- style={{
31
- width: '100%',
32
- padding: '8px 12px',
33
- border: `1px solid ${input.error ? '#d32f2f' : '#ccc'}`,
34
- borderRadius: '4px',
35
- fontSize: '14px',
36
- outline: 'none',
37
- }}
38
- placeholder="Type something..."
39
- />
40
- {input.error && (
41
- <div style={{ color: '#d32f2f', fontSize: '12px', marginTop: '4px' }}>
42
- {input.error}
43
- </div>
44
- )}
45
- <div style={{ marginTop: '12px', display: 'flex', gap: '8px' }}>
46
- <Button size="small" onClick={input.reset}>
47
- Reset
48
- </Button>
49
- <div style={{
50
- padding: '8px',
51
- fontSize: '12px',
52
- color: input.isValid ? '#2e7d32' : '#666'
53
- }}>
54
- Valid: {input.isValid ? 'Yes' : 'No'}
55
- </div>
56
- </div>
57
- </div>
58
- );
59
- };
60
-
61
- const meta: Meta<typeof FormInputDemo> = {
62
- title: 'Hooks/useFormInput',
63
- component: FormInputDemo,
64
- tags: ['autodocs'],
65
- parameters: {
66
- docs: {
67
- description: {
68
- component: 'A custom hook for managing form input state, validation, and error handling.',
69
- },
70
- },
71
- },
72
- };
73
-
74
- export default meta;
75
- type Story = StoryObj<typeof FormInputDemo>;
76
-
77
- export const Basic: Story = {
78
- args: {
79
- label: 'Basic Input',
80
- },
81
- };
82
-
83
- export const WithInitialValue: Story = {
84
- args: {
85
- label: 'Input with Initial Value',
86
- initialValue: 'Hello World',
87
- },
88
- };
89
-
90
- export const WithValidation: Story = {
91
- args: {
92
- label: 'Email Input (with validation)',
93
- initialValue: '',
94
- validate: (value) => {
95
- if (!value) return 'Email is required';
96
- if (!/\S+@\S+\.\S+/.test(value)) return 'Invalid email format';
97
- return null;
98
- },
99
- },
100
- };
101
-
102
- export const PasswordValidation: Story = {
103
- args: {
104
- label: 'Password (min 8 characters)',
105
- initialValue: '',
106
- validate: (value) => {
107
- if (!value) return 'Password is required';
108
- if (value.length < 8) return 'Password must be at least 8 characters';
109
- return null;
110
- },
111
- },
112
- };
113
-
114
- export const NumberValidation: Story = {
115
- args: {
116
- label: 'Age (must be 18+)',
117
- initialValue: '',
118
- validate: (value) => {
119
- if (!value) return 'Age is required';
120
- const num = parseInt(value, 10);
121
- if (isNaN(num)) return 'Must be a number';
122
- if (num < 18) return 'Must be 18 or older';
123
- return null;
124
- },
125
- },
126
- };
@@ -1,114 +0,0 @@
1
- import { useState, useCallback, ChangeEvent } from 'react';
2
-
3
- export interface UseFormInputOptions {
4
- initialValue?: string;
5
- validate?: (value: string) => string | null;
6
- onChange?: (value: string) => void;
7
- }
8
-
9
- export interface UseFormInputReturn {
10
- value: string;
11
- error: string | null;
12
- onChange: (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
13
- onBlur: () => void;
14
- setValue: (value: string) => void;
15
- setError: (error: string | null) => void;
16
- reset: () => void;
17
- isValid: boolean;
18
- }
19
-
20
- /**
21
- * Custom hook for managing form input state, validation, and error handling
22
- *
23
- * @param options - Configuration options for the hook
24
- * @param options.initialValue - Initial value for the input (default: '')
25
- * @param options.validate - Validation function that returns error message or null
26
- * @param options.onChange - Optional callback when value changes
27
- * @returns Object containing input state and handlers
28
- *
29
- * @example
30
- * ```tsx
31
- * const emailInput = useFormInput({
32
- * initialValue: '',
33
- * validate: (value) => {
34
- * if (!value) return 'Email is required';
35
- * if (!/\S+@\S+\.\S+/.test(value)) return 'Invalid email format';
36
- * return null;
37
- * }
38
- * });
39
- *
40
- * <input
41
- * value={emailInput.value}
42
- * onChange={emailInput.onChange}
43
- * onBlur={emailInput.onBlur}
44
- * />
45
- * {emailInput.error && <span>{emailInput.error}</span>}
46
- * ```
47
- */
48
- export const useFormInput = ({
49
- initialValue = '',
50
- validate,
51
- onChange: onChangeCallback,
52
- }: UseFormInputOptions = {}): UseFormInputReturn => {
53
- const [value, setValueState] = useState<string>(initialValue);
54
- const [error, setErrorState] = useState<string | null>(null);
55
- const [touched, setTouched] = useState<boolean>(false);
56
-
57
- const handleChange = useCallback(
58
- (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
59
- const newValue = e.target.value;
60
- setValueState(newValue);
61
-
62
- // Clear error when user starts typing
63
- if (error && touched) {
64
- const validationError = validate ? validate(newValue) : null;
65
- setErrorState(validationError);
66
- }
67
-
68
- // Call optional onChange callback
69
- if (onChangeCallback) {
70
- onChangeCallback(newValue);
71
- }
72
- },
73
- [error, touched, validate, onChangeCallback]
74
- );
75
-
76
- const handleBlur = useCallback(() => {
77
- setTouched(true);
78
- if (validate) {
79
- const validationError = validate(value);
80
- setErrorState(validationError);
81
- }
82
- }, [value, validate]);
83
-
84
- const setValue = useCallback((newValue: string) => {
85
- setValueState(newValue);
86
- if (touched && validate) {
87
- const validationError = validate(newValue);
88
- setErrorState(validationError);
89
- }
90
- }, [touched, validate]);
91
-
92
- const setError = useCallback((errorMessage: string | null) => {
93
- setErrorState(errorMessage);
94
- }, []);
95
-
96
- const reset = useCallback(() => {
97
- setValueState(initialValue);
98
- setErrorState(null);
99
- setTouched(false);
100
- }, [initialValue]);
101
-
102
- const isValid = error === null;
103
-
104
- return {
105
- value,
106
- error,
107
- onChange: handleChange,
108
- onBlur: handleBlur,
109
- setValue,
110
- setError,
111
- reset,
112
- isValid,
113
- };
114
- };
package/src/index.ts DELETED
@@ -1,11 +0,0 @@
1
- // Export all components
2
- export * from './components';
3
-
4
- // Export all hooks
5
- export * from './hooks';
6
-
7
- // Export theme
8
- export * from './theme';
9
-
10
- // Export utils
11
- export * from './utils/styles';
@@ -1,34 +0,0 @@
1
- import React, { createContext, useContext, ReactNode } from 'react';
2
- import { Theme } from './types';
3
-
4
- const ThemeContext = createContext<Theme | undefined>(undefined);
5
-
6
- export const useTheme = (): Theme => {
7
- const theme = useContext(ThemeContext);
8
- if (!theme) {
9
- throw new Error('useTheme must be used within a ThemeProvider');
10
- }
11
- return theme;
12
- };
13
-
14
- interface ThemeProviderProps {
15
- theme: Theme;
16
- children: ReactNode;
17
- }
18
-
19
- export const ThemeProvider: React.FC<ThemeProviderProps> = ({ theme, children }) => {
20
- return (
21
- <ThemeContext.Provider value={theme}>
22
- <div
23
- style={{
24
- color: theme.palette.text.primary,
25
- backgroundColor: theme.palette.background.default,
26
- fontFamily: theme.typography.fontFamily,
27
- fontSize: theme.typography.fontSize,
28
- }}
29
- >
30
- {children}
31
- </div>
32
- </ThemeContext.Provider>
33
- );
34
- };