# User-Defined Colors

User-defined colors allow you to extend the default color palette with custom colors that persist across your application. Users can add their own colors to color pickers for text, backgrounds, and borders.

## Overview

By default, color selectors in the toolbar (background color, text color, border color) provide a standard palette. User-defined colors extend this palette with:

* **Custom brand colors**: Add your organization's brand colors
* **Project-specific palettes**: Create color schemes for different projects
* **User preferences**: Allow users to save frequently used colors
* **Persistent colors**: Colors that persist across sessions

## Basic Usage

```tsx
import React, { useState } from "react";
import {
  SpreadsheetProvider,
  CanvasGrid,
  Toolbar,
  BackgroundColorSelector,
  TextColorSelector,
  BorderSelector,
} from "@rowsncolumns/spreadsheet";

function SpreadsheetWithCustomColors() {
  const [userDefinedColors, setUserDefinedColors] = useState<string[]>([
    "#FF6B6B", // Coral Red
    "#4ECDC4", // Turquoise
    "#45B7D1", // Sky Blue
    "#FFA07A", // Light Salmon
  ]);

  const handleAddColor = (color: string) => {
    setUserDefinedColors((prev) => [...prev, color]);
  };

  return (
    <SpreadsheetProvider>
      <Toolbar>
        <BackgroundColorSelector
          color={currentCellFormat?.backgroundColor}
          theme={theme}
          userDefinedColors={userDefinedColors}
          onAddUserDefinedColor={handleAddColor}
          onChange={onChangeBackgroundColor}
        />

        <TextColorSelector
          color={currentCellFormat?.textFormat?.color}
          theme={theme}
          isDarkMode={isDarkMode}
          userDefinedColors={userDefinedColors}
          onAddUserDefinedColor={handleAddColor}
          onChange={onChangeTextColor}
        />

        <BorderSelector
          borders={currentCellFormat?.borders}
          theme={theme}
          isDarkMode={isDarkMode}
          userDefinedColors={userDefinedColors}
          onAddUserDefinedColor={handleAddColor}
          onChange={onChangeBorder}
        />
      </Toolbar>

      <CanvasGrid
        // ... props
      />
    </SpreadsheetProvider>
  );
}
```

## State Management

### Simple State

Use React state for basic color management:

```tsx
function MySpreadsheet() {
  const [userDefinedColors, setUserDefinedColors] = useState<string[]>([]);

  const addColor = (color: string) => {
    setUserDefinedColors((prev) => [...prev, color]);
  };

  return (
    <BackgroundColorSelector
      userDefinedColors={userDefinedColors}
      onAddUserDefinedColor={addColor}
      // ... other props
    />
  );
}
```

### Persistent Storage

Save colors to localStorage for persistence:

```tsx
function MySpreadsheet() {
  const [userDefinedColors, setUserDefinedColors] = useState<string[]>(() => {
    // Load from localStorage on mount
    const saved = localStorage.getItem("spreadsheet-custom-colors");
    return saved ? JSON.parse(saved) : [];
  });

  const addColor = (color: string) => {
    setUserDefinedColors((prev) => {
      const updated = [...prev, color];
      // Save to localStorage
      localStorage.setItem(
        "spreadsheet-custom-colors",
        JSON.stringify(updated)
      );
      return updated;
    });
  };

  return (
    <BackgroundColorSelector
      userDefinedColors={userDefinedColors}
      onAddUserDefinedColor={addColor}
      // ... other props
    />
  );
}
```

### Server-Side Persistence

Save colors to your backend:

```tsx
function MySpreadsheet() {
  const [userDefinedColors, setUserDefinedColors] = useState<string[]>([]);

  // Load colors from server
  useEffect(() => {
    async function loadColors() {
      const response = await fetch("/api/user/colors");
      const colors = await response.json();
      setUserDefinedColors(colors);
    }
    loadColors();
  }, []);

  const addColor = async (color: string) => {
    // Optimistically update UI
    setUserDefinedColors((prev) => [...prev, color]);

    // Save to server
    try {
      await fetch("/api/user/colors", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ color }),
      });
    } catch (error) {
      console.error("Failed to save color:", error);
      // Revert on error
      setUserDefinedColors((prev) => prev.filter((c) => c !== color));
    }
  };

  return (
    <BackgroundColorSelector
      userDefinedColors={userDefinedColors}
      onAddUserDefinedColor={addColor}
      // ... other props
    />
  );
}
```

## Color Format

User-defined colors support various formats:

```tsx
const [userDefinedColors, setUserDefinedColors] = useState([
  "#FF6B6B",           // Hex
  "rgb(78, 205, 196)", // RGB
  "rgba(78, 205, 196, 0.8)", // RGBA
  "hsl(180, 60%, 55%)", // HSL
]);
```

## Complete Example

```tsx
import React, { useState, useEffect } from "react";
import {
  SpreadsheetProvider,
  CanvasGrid,
  Toolbar,
  BackgroundColorSelector,
  TextColorSelector,
  BorderSelector,
  Sheet,
  SpreadsheetTheme,
  defaultSpreadsheetTheme,
} from "@rowsncolumns/spreadsheet";
import {
  useSpreadsheetState,
  SheetData,
  CellData,
} from "@rowsncolumns/spreadsheet-state";

// Default custom colors
const DEFAULT_CUSTOM_COLORS = [
  "#FF6B6B", // Coral Red
  "#4ECDC4", // Turquoise
  "#45B7D1", // Sky Blue
  "#FFA07A", // Light Salmon
  "#96CEB4", // Sage Green
  "#FFEAA7", // Warm Yellow
  "#DFE6E9", // Light Gray
  "#74B9FF", // Soft Blue
];

function SpreadsheetWithColors() {
  const [sheets, setSheets] = useState<Sheet[]>([
    { sheetId: 1, rowCount: 100, columnCount: 26, title: "Sheet 1" }
  ]);
  const [sheetData, setSheetData] = useState<SheetData<CellData>>({});
  const [theme] = useState<SpreadsheetTheme>(defaultSpreadsheetTheme);

  // Load user-defined colors from localStorage
  const [userDefinedColors, setUserDefinedColors] = useState<string[]>(() => {
    const saved = localStorage.getItem("user-colors");
    return saved ? JSON.parse(saved) : DEFAULT_CUSTOM_COLORS;
  });

  const {
    activeCell,
    activeSheetId,
    selections,
    getCellData,
    getEffectiveFormat,
    onChangeActiveCell,
    onChangeSelections,
    onChange,
    onChangeFormatting,
    onChangeBorder,
    isDarkMode,
  } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheets: setSheets,
    onChangeSheetData: setSheetData,
  });

  // Get current cell format
  const currentCellFormat = getEffectiveFormat(
    activeSheetId,
    activeCell.rowIndex,
    activeCell.columnIndex
  );

  // Handle adding new color
  const handleAddColor = (color: string) => {
    setUserDefinedColors((prev) => {
      // Avoid duplicates
      if (prev.includes(color)) return prev;

      const updated = [...prev, color];

      // Save to localStorage
      localStorage.setItem("user-colors", JSON.stringify(updated));

      return updated;
    });
  };

  // Handle background color change
  const handleBackgroundColorChange = (color: any) => {
    onChangeFormatting(
      activeSheetId,
      activeCell,
      selections,
      "backgroundColor",
      color
    );
  };

  // Handle text color change
  const handleTextColorChange = (color: any) => {
    onChangeFormatting(
      activeSheetId,
      activeCell,
      selections,
      "textFormat",
      { color }
    );
  };

  return (
    <SpreadsheetProvider>
      <div className="flex flex-col h-screen">
        <Toolbar>
          <BackgroundColorSelector
            color={currentCellFormat?.backgroundColor}
            theme={theme}
            userDefinedColors={userDefinedColors}
            onAddUserDefinedColor={handleAddColor}
            onChange={handleBackgroundColorChange}
          />

          <TextColorSelector
            color={currentCellFormat?.textFormat?.color}
            theme={theme}
            isDarkMode={isDarkMode}
            userDefinedColors={userDefinedColors}
            onAddUserDefinedColor={handleAddColor}
            onChange={handleTextColorChange}
          />

          <BorderSelector
            borders={currentCellFormat?.borders}
            theme={theme}
            isDarkMode={isDarkMode}
            userDefinedColors={userDefinedColors}
            onAddUserDefinedColor={handleAddColor}
            onChange={onChangeBorder}
          />
        </Toolbar>

        <div className="flex-1">
          <CanvasGrid
            sheetId={activeSheetId}
            activeCell={activeCell}
            selections={selections}
            getCellData={getCellData}
            onChangeActiveCell={onChangeActiveCell}
            onChangeSelections={onChangeSelections}
            onChange={onChange}
            theme={theme}
          />
        </div>
      </div>
    </SpreadsheetProvider>
  );
}

export default SpreadsheetWithColors;
```

## Advanced Features

### Color Validation

Validate colors before adding them:

```tsx
function isValidColor(color: string): boolean {
  const style = new Option().style;
  style.color = color;
  return style.color !== "";
}

const addColor = (color: string) => {
  if (!isValidColor(color)) {
    console.error("Invalid color:", color);
    return;
  }

  setUserDefinedColors((prev) => [...prev, color]);
};
```

### Color Limits

Limit the number of custom colors:

```tsx
const MAX_CUSTOM_COLORS = 20;

const addColor = (color: string) => {
  setUserDefinedColors((prev) => {
    if (prev.length >= MAX_CUSTOM_COLORS) {
      console.warn("Maximum custom colors reached");
      return prev;
    }
    return [...prev, color];
  });
};
```

### Color Organization

Organize colors by category:

```tsx
const [colorCategories, setColorCategories] = useState({
  brand: ["#FF6B6B", "#4ECDC4"],
  pastels: ["#FFA07A", "#96CEB4"],
  vibrant: ["#45B7D1", "#FFEAA7"],
});

// Flatten for color selectors
const allColors = Object.values(colorCategories).flat();
```

### Remove Colors

Allow users to remove custom colors:

```tsx
function ColorManager() {
  const [userDefinedColors, setUserDefinedColors] = useState<string[]>([]);

  const removeColor = (colorToRemove: string) => {
    setUserDefinedColors((prev) =>
      prev.filter((color) => color !== colorToRemove)
    );
  };

  return (
    <div className="space-y-2">
      {userDefinedColors.map((color) => (
        <div key={color} className="flex items-center gap-2">
          <div
            className="w-8 h-8 rounded border"
            style={{ backgroundColor: color }}
          />
          <span className="flex-1">{color}</span>
          <button
            onClick={() => removeColor(color)}
            className="px-2 py-1 text-sm text-red-600"
          >
            Remove
          </button>
        </div>
      ))}
    </div>
  );
}
```

## Integration with Theme Colors

User-defined colors work alongside theme colors:

```tsx
import { SpreadsheetTheme } from "@rowsncolumns/spreadsheet";

const customTheme: SpreadsheetTheme = {
  ...defaultSpreadsheetTheme,
  colors: [
    "#000000", // Black
    "#FFFFFF", // White
    "#FF0000", // Red
    // ... theme colors
  ],
};

// User-defined colors appear separately from theme colors
const [userDefinedColors, setUserDefinedColors] = useState([
  "#FF6B6B",
  "#4ECDC4",
]);
```

## Use Cases

### Brand Colors

```tsx
const brandColors = [
  "#1E40AF", // Primary Blue
  "#F59E0B", // Accent Orange
  "#10B981", // Success Green
  "#EF4444", // Error Red
];

const [userDefinedColors, setUserDefinedColors] = useState(brandColors);
```

### Project-Specific Palettes

```tsx
const projectPalettes = {
  marketing: ["#FF6B6B", "#4ECDC4", "#FFA07A"],
  finance: ["#1E40AF", "#10B981", "#F59E0B"],
  design: ["#8B5CF6", "#EC4899", "#F59E0B"],
};

const [activeProject, setActiveProject] = useState("marketing");
const userDefinedColors = projectPalettes[activeProject];
```

### User Preferences

```tsx
// Load user's saved colors from profile
const { user } = useAuth();
const [userDefinedColors, setUserDefinedColors] = useState(
  user.preferences.customColors || []
);
```

## Best Practices

1. **Provide Defaults**: Start with a set of useful default colors
2. **Limit Count**: Cap custom colors at 10-20 to avoid clutter
3. **Validate Input**: Ensure colors are valid before adding
4. **Persist Data**: Save colors to localStorage or backend
5. **Avoid Duplicates**: Check for duplicate colors before adding
6. **Visual Feedback**: Show color swatches clearly in the UI
7. **Accessibility**: Ensure sufficient contrast for readability

## Troubleshooting

### Colors Not Appearing

* Verify the `userDefinedColors` array is properly formatted
* Check that color values are valid CSS colors
* Ensure the array is passed to all color selector components

### Colors Not Persisting

* Confirm localStorage save logic is working
* Check for browser storage limits
* Verify localStorage keys are consistent

### Duplicate Colors

* Implement duplicate checking before adding colors
* Use `Set` to ensure uniqueness if needed

```tsx
const addColor = (color: string) => {
  setUserDefinedColors((prev) => {
    if (prev.includes(color)) return prev;
    return [...prev, color];
  });
};
```
