# React Rich Text Editor

[![npm version](https://badge.fury.io/js/react-summernote-light.svg)](https://badge.fury.io/js/react-summernote-light)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A powerful, customizable WYSIWYG editor for React applications built from scratch with modern design and extensive features.

## ✨ Features

- 🎨 **Modern Design** - Beautiful UI with Tailwind CSS and smooth animations
- 📝 **Rich Text Editing** - Bold, italic, underline, headings, lists, alignment, and more
- 🖼️ **Media Support** - Image and video embedding with custom upload handlers
- 📊 **Tables** - Full table creation and editing capabilities
- 🎯 **Font Sizes** - Immediate font size application with visual feedback
- 💻 **Code View** - Toggle between WYSIWYG and HTML code editing (powered by CodeMirror)
- 🖥️ **Fullscreen Mode** - Distraction-free editing experience
- 🌙 **Theme Support** - Light and dark theme compatibility
- 🔧 **Highly Customizable** - Extensive props and configuration options
- 📱 **Responsive** - Works seamlessly on desktop and mobile devices
- ⚡ **TypeScript** - Full TypeScript support with type definitions

## 🚀 Installation

```bash
npm install react-summernote-light
```

### Peer Dependencies

This package requires the following peer dependencies:

```bash
npm install react react-dom @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-popover @radix-ui/react-select @radix-ui/react-separator @radix-ui/react-slot @radix-ui/react-tabs @radix-ui/react-toggle @radix-ui/react-tooltip @uiw/react-codemirror lucide-react class-variance-authority clsx tailwind-merge
```

### Tailwind CSS Setup

This component requires Tailwind CSS. Make sure you have it configured in your project:

1. **Install Tailwind CSS** (if not already installed):

```bash
npm install tailwindcss
```

2. **Configure Tailwind** to include the package styles in your `tailwind.config.js`:

```js
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
    "./node_modules/react-summernote-light/dist/**/*.{js,jsx}", // Add this line
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
```

## 📖 Quick Start

### Basic Usage

```tsx
import React, { useState } from "react";
import { RichTextEditor } from "react-summernote-light";

function App() {
  const [content, setContent] = useState("");

  return (
    <div className="p-4">
      <RichTextEditor
        initialValue={content}
        onChange={setContent}
        placeholder="Start typing your content here..."
        minHeight="400px"
      />
    </div>
  );
}

export default App;
```

## 🛠️ Toolbar Customization

The editor provides extensive toolbar customization through the `toolbarConfig` prop. You can customize which buttons appear, how they're grouped, and hide specific buttons.

### Available Buttons

| Button Type            | Description                       |
| ---------------------- | --------------------------------- |
| `undo`                 | Undo action                       |
| `redo`                 | Redo action                       |
| `formatBlock`          | Heading/paragraph format selector |
| `fontName`             | Font family selector              |
| `fontSize`             | Font size selector                |
| `bold`                 | Bold formatting                   |
| `italic`               | Italic formatting                 |
| `underline`            | Underline formatting              |
| `strikethrough`        | Strikethrough formatting          |
| `foreColor`            | Text color picker                 |
| `backColor`            | Background color picker           |
| `justifyLeft`          | Align left                        |
| `justifyCenter`        | Align center                      |
| `justifyRight`         | Align right                       |
| `justifyFull`          | Justify text                      |
| `insertUnorderedList`  | Bullet list                       |
| `insertOrderedList`    | Numbered list                     |
| `indent`               | Increase indent                   |
| `outdent`              | Decrease indent                   |
| `createLink`           | Insert link                       |
| `insertImage`          | Insert image                      |
| `insertVideo`          | Insert video                      |
| `insertTable`          | Insert table                      |
| `insertHorizontalRule` | Insert horizontal line            |
| `modeToggle`           | Toggle between visual/code view   |

### Toolbar Configuration

```tsx
interface ToolbarConfig {
  sections?: ToolbarSection[];
  hiddenButtons?: ToolbarButtonType[];
  showSeparators?: boolean;
}

interface ToolbarSection {
  name: string;
  buttons: ToolbarButtonType[];
}
```

### Example: Custom Toolbar Sections

```tsx
<RichTextEditor
  toolbarConfig={{
    sections: [
      {
        name: "text-style",
        buttons: ["bold", "italic", "underline", "foreColor"],
      },
      {
        name: "lists",
        buttons: ["insertUnorderedList", "insertOrderedList"],
      },
      {
        name: "insert",
        buttons: ["createLink", "insertImage"],
      },
    ],
    showSeparators: true,
    hiddenButtons: [],
  }}
/>
```

### Example: Minimal Toolbar

```tsx
<RichTextEditor
  toolbarConfig={{
    sections: [
      {
        name: "basic",
        buttons: ["bold", "italic", "createLink"],
      },
    ],
    showSeparators: false,
  }}
/>
```

### Example: Hide Specific Buttons

```tsx
<RichTextEditor
  toolbarConfig={{
    hiddenButtons: ["insertVideo", "insertTable", "foreColor", "backColor"],
  }}
/>
```

### Example: No Toolbar

```tsx
<RichTextEditor
  showToolbar={false}
  placeholder="Clean editor without toolbar..."
/>
```

### Advanced Usage

```tsx
import React, { useState } from "react";
import { RichTextEditor, RichTextEditorProps } from "react-summernote-light";

function AdvancedEditor() {
  const [content, setContent] = useState(
    "<h2>Welcome!</h2><p>Rich text content here...</p>"
  );

  // Custom image upload handler
  const handleImageUpload = async (file: File): Promise<string> => {
    const formData = new FormData();
    formData.append("image", file);

    try {
      const response = await fetch("/api/upload", {
        method: "POST",
        body: formData,
      });

      const data = await response.json();
      return data.url; // Return the uploaded image URL
    } catch (error) {
      console.error("Upload failed:", error);
      throw error;
    }
  };

  return (
    <div className="max-w-4xl mx-auto p-6">
      <h1 className="text-2xl font-bold mb-4">Rich Text Editor</h1>

      <RichTextEditor
        initialValue={content}
        onChange={setContent}
        onImageUpload={handleImageUpload}
        placeholder="Start writing something amazing..."
        minHeight="500px"
        enableCodeView
        enableFullscreen
        theme="light"
        className="border rounded-lg shadow-sm"
        onFocus={() => console.log("Editor focused")}
        onBlur={() => console.log("Editor blurred")}
      />

      <div className="mt-4 p-4 bg-gray-100 rounded">
        <h3 className="font-semibold mb-2">Content Preview:</h3>
        <div dangerouslySetInnerHTML={{ __html: content }} />
      </div>
    </div>
  );
}

export default AdvancedEditor;
```

## 🔧 API Reference

### Props

| Prop               | Type                              | Default   | Description                                                       |
| ------------------ | --------------------------------- | --------- | ----------------------------------------------------------------- |
| `initialValue`     | `string`                          | `''`      | Initial HTML content of the editor                                |
| `onChange`         | `(content: string) => void`       | -         | Callback fired when content changes                               |
| `onFocus`          | `() => void`                      | -         | Callback fired when editor gains focus                            |
| `onBlur`           | `() => void`                      | -         | Callback fired when editor loses focus                            |
| `onImageUpload`    | `(file: File) => Promise<string>` | -         | Custom image upload handler. Should return the uploaded image URL |
| `placeholder`      | `string`                          | -         | Placeholder text shown when editor is empty                       |
| `className`        | `string`                          | -         | Additional CSS classes for the editor container                   |
| `height`           | `string`                          | -         | Fixed height of the editor                                        |
| `minHeight`        | `string`                          | `'300px'` | Minimum height of the editor                                      |
| `maxHeight`        | `string`                          | -         | Maximum height of the editor                                      |
| `theme`            | `'light' \| 'dark'`               | `'light'` | Editor theme                                                      |
| `enableCodeView`   | `boolean`                         | `false`   | Enable HTML code view toggle                                      |
| `enableFullscreen` | `boolean`                         | `false`   | Enable fullscreen mode                                            |
| `disableImages`    | `boolean`                         | `false`   | Disable image insertion functionality                             |
| `disableVideos`    | `boolean`                         | `false`   | Disable video embedding functionality                             |
| `disableTables`    | `boolean`                         | `false`   | Disable table creation and editing                                |
| `airMode`          | `boolean`                         | `false`   | Enable air mode (minimal styling)                                 |
| `toolbarConfig`    | `ToolbarConfig`                   | -         | Custom toolbar configuration                                      |
| `showToolbar`      | `boolean`                         | `true`    | Show/hide the toolbar completely                                  |

### Types

```tsx
interface RichTextEditorProps {
  initialValue?: string;
  onChange?: (content: string) => void;
  onFocus?: () => void;
  onBlur?: () => void;
  onImageUpload?: (file: File) => Promise<string>;
  placeholder?: string;
  toolbarItems?: ToolbarButton[];
  disableImages?: boolean;
  disableVideos?: boolean;
  disableTables?: boolean;
  className?: string;
  height?: string;
  minHeight?: string;
  maxHeight?: string;
  theme?: "light" | "dark";
  airMode?: boolean;
  enableCodeView?: boolean;
  enableFullscreen?: boolean;
}

interface ToolbarButton {
  icon: string;
  tooltip: string;
  command: string;
  value?: string;
}
```

## 🎨 Styling and Customization

### Custom Styling

You can customize the editor's appearance using CSS classes:

```tsx
<RichTextEditor
  className="custom-editor border-2 border-blue-500 rounded-xl"
  // ... other props
/>
```

### Theme Customization

The editor supports light and dark themes:

```tsx
<RichTextEditor
  theme="dark"
  className="bg-gray-900 text-white"
  // ... other props
/>
```

### Custom Toolbar

You can customize the toolbar by providing your own toolbar items:

```tsx
const customToolbar = [
  { icon: "bold", tooltip: "Bold", command: "bold" },
  { icon: "italic", tooltip: "Italic", command: "italic" },
  // ... more items
];

<RichTextEditor
  toolbarItems={customToolbar}
  // ... other props
/>;
```

## 📝 Usage Examples

### With Form Libraries

#### React Hook Form

```tsx
import { useForm, Controller } from "react-hook-form";

function MyForm() {
  const { control, handleSubmit } = useForm();

  const onSubmit = (data) => {
    console.log(data.content);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="content"
        control={control}
        render={({ field }) => (
          <RichTextEditor
            initialValue={field.value}
            onChange={field.onChange}
            placeholder="Enter your content..."
          />
        )}
      />
      <button type="submit">Submit</button>
    </form>
  );
}
```

#### Formik

```tsx
import { Formik, Form, Field } from "formik";

function MyFormikForm() {
  return (
    <Formik
      initialValues={{ content: "" }}
      onSubmit={(values) => console.log(values)}
    >
      {({ setFieldValue, values }) => (
        <Form>
          <RichTextEditor
            initialValue={values.content}
            onChange={(content) => setFieldValue("content", content)}
            placeholder="Enter your content..."
          />
          <button type="submit">Submit</button>
        </Form>
      )}
    </Formik>
  );
}
```

### Image Upload Integration

#### With Firebase Storage

```tsx
import { getStorage, ref, uploadBytes, getDownloadURL } from "firebase/storage";

const storage = getStorage();

const handleImageUpload = async (file: File): Promise<string> => {
  const storageRef = ref(storage, `images/${file.name}`);
  const snapshot = await uploadBytes(storageRef, file);
  const downloadURL = await getDownloadURL(snapshot.ref);
  return downloadURL;
};

<RichTextEditor
  onImageUpload={handleImageUpload}
  // ... other props
/>;
```

#### With AWS S3

```tsx
import AWS from "aws-sdk";

const s3 = new AWS.S3({
  accessKeyId: "your-access-key",
  secretAccessKey: "your-secret-key",
  region: "your-region",
});

const handleImageUpload = async (file: File): Promise<string> => {
  const params = {
    Bucket: "your-bucket-name",
    Key: `images/${file.name}`,
    Body: file,
    ContentType: file.type,
  };

  const result = await s3.upload(params).promise();
  return result.Location;
};

<RichTextEditor
  onImageUpload={handleImageUpload}
  // ... other props
/>;
```

## 🛠️ Development

### Prerequisites

- Node.js 16+
- npm or yarn

### Local Development

1. Clone the repository:

```bash
git clone https://github.com/shakibgithub944/flexi-rich-text.git
cd flexi-rich-text
```

2. Install dependencies:

```bash
npm install
```

3. Start the development server:

```bash
npm run dev
```

4. Open http://localhost:8080

### Building the Package

```bash
# Build the library
npm run build:lib

# Build the demo app
npm run build:app

# Build both
npm run build
```

### Testing the Package Locally

```bash
# Create a package tarball
npm pack

# In another project, install the local package
npm install path/to/react-summernote-light-1.0.0.tgz
```

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

## 📝 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🐛 Issues

If you encounter any issues or have suggestions, please [open an issue](https://github.com/shakibgithub944/flexi-rich-text/issues) on GitHub.

## 🙏 Acknowledgments

- Built with [React](https://reactjs.org/)
- UI components from [Radix UI](https://www.radix-ui.com/)
- Styling with [Tailwind CSS](https://tailwindcss.com/)
- Code editing with [CodeMirror](https://codemirror.net/)
- Icons from [Lucide React](https://lucide.dev/)

---

Made with ❤️ by [shakibgithub944](https://github.com/shakibgithub944)
