Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}
"extends": [
"next/core-web-vitals",
"next/typescript",
"plugin:storybook/recommended"
]
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

*storybook.log
storybook-static
19 changes: 19 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { StorybookConfig } from '@storybook/nextjs';

const config: StorybookConfig = {
stories: ['../components/**/*stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y',
'@storybook/addon-docs',
],
framework: {
name: '@storybook/nextjs',
options: {},
},
docs: {
autodocs: 'tag',
},
};

export default config;
16 changes: 16 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { withThemeByClassName } from '@storybook/addon-styling';
import '../app/globals.css';

export const decorators = [
(Story: any) => (
<div style={{ padding: 16 }}>
<Story />
</div>
),
];

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: { matchers: { color: /(background|color)$/i, date: /Date$/ } },
};
11 changes: 11 additions & 0 deletions .storybook/story-template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

type ModuleType = Record<string, any>;

export function Template({ module, props }: { module: ModuleType; props?: any }) {
const Component = module?.Root || module?.default || Object.values(module).find((v) => typeof v === 'function');
if (!Component) return null;
return <Component {...(props ?? {})} />;
}

export default Template;
6 changes: 6 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400;0,500;700;1,400&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
Expand Down Expand Up @@ -390,3 +391,8 @@
transform: scale(0.8996);
transform-origin: center;
}

/* Set default font to Inter */
html, body {
font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
}
9 changes: 9 additions & 0 deletions components/ui/accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './accordion';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Accordion' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Panel' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/alert.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './alert';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Alert' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Alert message' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/avatar-empty-icons.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './avatar-empty-icons';
import Template from '../../.storybook/story-template';

export default { title: 'UI/AvatarEmptyIcons' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Empty' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/avatar-group-compact.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './avatar-group-compact';
import Template from '../../.storybook/story-template';

export default { title: 'UI/AvatarGroupCompact' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'A,B' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/avatar-group.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './avatar-group';
import Template from '../../.storybook/story-template';

export default { title: 'UI/AvatarGroup' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'A, B' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/avatar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './avatar';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Avatar' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'A' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/badge.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './badge';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Badge' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Badge' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/breadcrumb.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './breadcrumb';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Breadcrumb' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Home / Section' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/button-group.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './button-group';
import Template from '../../.storybook/story-template';

export default { title: 'UI/ButtonGroup' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Btn1 Btn2' }} />,
};
98 changes: 98 additions & 0 deletions components/ui/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Root } from './button'; // ← این مهمه: Root نه Button
import { Icon } from './button';

// یا می‌تونی اینجوری هم بنویسی (هر دو درسته):
// import { Root as Button, Icon } from './button';

const meta = {
title: 'UI/Button',
component: Root, // ← اینجا هم Root
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'neutral', 'error'],
},
mode: {
control: 'select',
options: ['filled', 'stroke', 'lighter', 'ghost'],
},
size: {
control: 'select',
options: ['medium', 'small', 'xsmall', 'xxsmall'],
},
asChild: { control: 'boolean' },
disabled: { control: 'boolean' },
},
} satisfies Meta<typeof Root>;

export default meta;
type Story = StoryObj<typeof meta>;

// استوری‌های مختلف
export const PrimaryFilled: Story = {
args: {
children: 'Primary Filled',
variant: 'primary',
mode: 'filled',
},
};

export const PrimaryStroke: Story = {
args: {
children: 'Primary Stroke',
variant: 'primary',
mode: 'stroke',
},
};

export const WithIcon: Story = {
args: {
children: (
<>
<Icon>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-5">
<path strokeLinecap="round" strokeLinejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z" />
</svg>
</Icon>
Like
</>
),
variant: 'primary',
mode: 'filled',
},
};

export const NeutralGhost: Story = {
args: {
children: 'Neutral Ghost',
variant: 'neutral',
mode: 'ghost',
},
};

export const ErrorFilled: Story = {
args: {
children: 'Delete',
variant: 'error',
mode: 'filled',
},
};

export const Small: Story = {
args: {
children: 'Small Button',
size: 'small',
},
};

export const Disabled: Story = {
args: {
children: 'Disabled',
disabled: true,
},
};
9 changes: 9 additions & 0 deletions components/ui/checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './checkbox';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Checkbox' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Checkbox' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/color-picker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './color-picker';
import Template from '../../.storybook/story-template';

export default { title: 'UI/ColorPicker' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Color' }} />,
};
38 changes: 24 additions & 14 deletions components/ui/color-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,27 +137,37 @@ const EyeDropperButton = React.forwardRef<
HTMLButtonElement,
React.HTMLAttributes<HTMLButtonElement>
>(({ ...rest }, forwardedRef) => {
const state = React.useContext(ColorPickerStateContext)!;
const state = React.useContext(ColorPickerStateContext);

// eslint-disable-next-line
// @ts-ignore
if (typeof EyeDropper === 'undefined') {
if (!state) return null;

// Access EyeDropper from global in a typed, safe way
const EyeDropperAPI = (globalThis as any).EyeDropper as
| { new (): { open: () => Promise<{ sRGBHex: string }> } }
| undefined;

if (!EyeDropperAPI) {
return null;
}

const handleClick = async () => {
try {
const picker = new EyeDropperAPI();
const result = await picker.open();
if (result?.sRGBHex) {
state.setColor(parseColor(result.sRGBHex));
}
} catch (err) {
// swallow errors silently or optionally surface a notification
// console.error('EyeDropper failed', err);
}
};

return (
<button
ref={forwardedRef}
aria-label='Eye dropper'
onClick={() => {
// eslint-disable-next-line
// @ts-ignore
new EyeDropper()
.open()
.then((result: { sRGBHex: string }) =>
state.setColor(parseColor(result.sRGBHex)),
);
}}
aria-label="Eye dropper"
onClick={handleClick}
{...rest}
/>
);
Expand Down
9 changes: 9 additions & 0 deletions components/ui/command-menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './command-menu';
import Template from '../../.storybook/story-template';

export default { title: 'UI/CommandMenu' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Open' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/compact-button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './compact-button';
import Template from '../../.storybook/story-template';

export default { title: 'UI/CompactButton' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Compact' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/datepicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './datepicker';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Datepicker' };

export const Default = {
render: () => <Template module={Module} props={{ children: 'Pick a date' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/digit-input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './digit-input';
import Template from '../../.storybook/story-template';

export default { title: 'UI/DigitInput' };

export const Default = {
render: () => <Template module={Module} props={{ value: '123' }} />,
};
9 changes: 9 additions & 0 deletions components/ui/divider.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import * as Module from './divider';
import Template from '../../.storybook/story-template';

export default { title: 'UI/Divider' };

export const Default = {
render: () => <Template module={Module} props={{ children: null }} />,
};
Loading