# Widgets Architecture

> **⚠️ Project-Side Implementation Required**
>
> Starting with v4.3.x, widgets should be implemented on the **project side** using the building blocks provided by this library. The library exports UI components, configuration helpers, and state management hooks—you compose them in your project to create complete widgets.
>
> **Benefits:**
>
> - Full customization tailored to your project's specific needs
> - Direct integration with your data sources and state management
> - Independent updates without waiting for library releases
> - Include only the widget features you actually need
>
> **See the internal Storybook for complete implementation examples** of all widget types (Bar, Pie, Histogram, Timeseries, Scatterplot, Formula, etc.).

---

This directory contains the CARTO PS widget system with an optimized architecture for code reuse and maintainability.

## Directory Structure

```
widgets/
├── utils/              # Shared utilities (exported as @carto/ps-react-ui/widgets/utils)
│   ├── chart-config/     # Chart widget configuration utilities
│   │   ├── config-factory.ts     # Factory for creating chart widget configs
│   │   ├── csv-modifiers.ts      # CSV export utilities
│   │   ├── option-builders.ts    # EChart option builders
│   │   └── index.ts             # Exports
│   └── skeleton/         # Shared skeleton styles
│       ├── styles.ts            # Base skeleton container styles
│       └── index.ts            # Exports
├── bar/                  # Bar chart widget
├── histogram/            # Histogram widget
├── pie/                  # Pie chart widget
├── scatterplot/          # Scatterplot widget
├── timeseries/           # Timeseries/line chart widget
├── formula/              # Formula/KPI widget
├── note/                 # Note widget
├── markdown/             # Markdown renderer
├── echart/               # Base EChart component
├── wrapper/              # Widget wrapper with header/actions
├── actions/              # Download, fullscreen actions
├── config-loader/        # Config loading system
├── skeleton-loader/      # Skeleton components
├── stores/               # Zustand widget state
├── loader/               # WidgetLoader container
└── widget/               # Main widget orchestrator
```

## Shared Utilities (`utils/`)

The `utils` directory contains shared utilities used by multiple widgets. These are exported as `@carto/ps-react-ui/widgets/utils`.

### Chart Config Factory

**File:** `utils/chart-config/config-factory.ts`

Creates standardized chart widget configurations, eliminating code duplication.

**Example:**

```typescript
import {
  createChartWidgetConfig,
  flattenObjectArrayToCSV,
} from '../utils/chart-config'

export const myWidgetConfig = createChartWidgetConfig({
  type: 'my-widget',
  getOptions: ({ data, theme }) => ({
    // EChart configuration
    legend: buildLegendConfig({ hasLegend }),
    // ...
  }),
  csvModifier: (data) => flattenObjectArrayToCSV(data),
})
```

### CSV Modifiers

**File:** `utils/chart-config/csv-modifiers.ts`

- `flattenObjectArrayToCSV()` - For widgets with object-based data (bar, pie, histogram, timeseries)
- `scatterplotDataToCSV()` - For scatterplot with array-based data

### EChart Option Builders

**File:** `utils/chart-config/option-builders.ts`

- `buildLegendConfig({ hasLegend, labelFormatter? })` - Standard legend configuration
- `buildGridConfig(hasLegend, theme, additionalConfig?)` - Grid with legend-aware spacing
- `createTooltipPositioner(theme)` - Tooltip positioning with overflow handling

### Skeleton Styles

**File:** `utils/skeleton/styles.ts`

- `baseSkeletonStyles.graph.container` - Base container styles for all chart skeletons

## Creating a New Chart Widget

1. **Create widget directory:**

   ```bash
   mkdir src/widgets/my-widget
   ```

2. **Create `types.ts`:**

   ```typescript
   import type { BaseWidgetProps } from '../widget/types'
   import type { BaseConfig } from '../config-loader'
   import type {
     EchartWidgetConfig,
     EchartWidgetData,
     EchartWidgetState,
   } from '../echart'

   export interface MyWidgetProps extends BaseWidgetProps<MyWidgetConfig> {
     type: 'my-widget'
     data: MyWidgetData | undefined
   }

   export type MyWidgetData = EchartWidgetData
   export type MyWidgetState = EchartWidgetState
   export type MyWidgetConfig = MyConfig &
     EchartWidgetConfig & { type: MyWidgetProps['type'] }

   export interface MyConfig extends Omit<BaseConfig, 'data'> {
     data?: MyWidgetData
   }
   ```

3. **Create `config.ts` using shared factory:**

   ```typescript
   import type { EchartOptionsProps } from '../echart'
   import type { MyConfig, MyWidgetConfig, MyWidgetData } from './types'
   import {
     createChartWidgetConfig,
     flattenObjectArrayToCSV,
     buildLegendConfig,
     buildGridConfig,
   } from '../utils/chart-config'

   export const myWidgetConfig = createChartWidgetConfig<
     MyWidgetData,
     MyConfig
   >({
     type: 'my-widget',
     getOptions,
     csvModifier: (data) => flattenObjectArrayToCSV(data),
   })

   function getOptions({
     data = [],
     theme,
   }: Omit<MyConfig, 'refUI'>): EchartOptionsProps {
     const hasLegend = (data?.length ?? 0) > 1
     return {
       legend: buildLegendConfig({ hasLegend }),
       grid: buildGridConfig(hasLegend, theme),
       // ... widget-specific configuration
     }
   }
   ```

4. **Create `skeleton.tsx` and `style.ts`:**

   ```typescript
   // style.ts
   import type { SxProps, Theme } from '@mui/material'
   import { baseSkeletonStyles } from '../utils/skeleton'

   export const styles = {
     skeleton: {
       graph: baseSkeletonStyles.graph,
       // Add widget-specific styles here
     },
   } satisfies Record<string, SxProps<Theme>>
   ```

5. **Create `index.ts`:**

   ```typescript
   export type {
     MyWidgetProps,
     MyWidgetData,
     MyWidgetState,
     MyWidgetConfig,
     MyConfig,
   } from './types'
   export { myWidgetConfig } from './config'
   export { MyWidgetSkeleton as Skeleton } from './skeleton'
   ```

6. **Run build automation:**

   ```bash
   pnpm generate:exports
   ```

   This will automatically:
   - Add the widget to vite.config.ts entry points
   - Add the widget to package.json exports
   - Validate all exports

## Build Automation

### Scripts

- `pnpm exports:validate` - Validate all package.json exports have corresponding source files

### How It Works

**package.json is the source of truth!**

The `scripts/generate-widget-config.ts` script:

1. Reads widget exports from `package.json`
2. Converts export paths to source file paths
3. Generates vite entry points dynamically
4. Validates that all exports have corresponding source files

The script uses **Node 22's native TypeScript support** with the `--experimental-strip-types` flag, so no compilation step is needed!

### Adding a New Widget

1. **Create widget folder and files:**

   ```bash
   mkdir src/widgets/my-widget
   # Create index.ts, config.ts, types.ts, etc.
   ```

2. **Add export to package.json:**

   ```json
   {
     "exports": {
       "./widgets/my-widget": {
         "import": "./dist/widgets/my-widget.js",
         "types": "./dist/types/widgets/my-widget/index.d.ts"
       }
     }
   }
   ```

3. **Build:**
   ```bash
   pnpm build
   ```

The vite config automatically reads from package.json and builds all declared exports!

## Code Reduction Achieved

| Widget                | Before     | After     | Reduction |
| --------------------- | ---------- | --------- | --------- |
| bar/config.ts         | ~150 lines | ~65 lines | ~57%      |
| histogram/config.ts   | ~155 lines | ~68 lines | ~56%      |
| pie/config.ts         | ~160 lines | ~50 lines | ~69%      |
| scatterplot/config.ts | ~145 lines | ~90 lines | ~38%      |
| timeseries/config.ts  | ~130 lines | ~70 lines | ~46%      |

**Total:** ~400-500 lines of duplicated code eliminated

## Benefits

✅ **Reduced Duplication** - Shared utilities eliminate repeated code
✅ **Better Maintainability** - Changes to common patterns update all widgets
✅ **Automated Builds** - No manual vite/package.json updates needed
✅ **Consistent Patterns** - All widgets follow the same structure
✅ **Type Safety** - Full TypeScript support throughout
✅ **Easy to Extend** - Adding new widgets is straightforward
✅ **Backwards Compatible** - All external imports remain unchanged
