# Data-Grist

Data-Grist is a powerful web component for displaying and manipulating data. It can present data in Grid, List, or Card formats and provides various features such as data sorting, filtering, editing, and more.

## Key Features

- **Multiple Display Modes**: Flexibly display the same data in grid, list, or card formats
- **Data Manipulation**: Add, modify, and delete records
- **Sorting and Filtering**: Single/multi-column sorting, various filtering options
- **Editing Capabilities**: Inline cell editing, validation
- **Pagination**: Standard paging and infinite scroll support
- **Selection**: Single/multiple record selection
- **State Tracking**: Track changed (dirty) records
- **Customization**: Support for various renderers, editors, and event handlers
- **Mobile Responsive**: Responsive design support

## Installation

```bash
npm install @operato/data-grist
```

## Basic Usage

```html
<script type="module">
  import '@operato/data-grist/ox-grist.js'
</script>

<ox-grist
  .mode=${'GRID'}
  .config=${gridConfig}
  .fetchHandler=${fetchDataFunction}
></ox-grist>
```

## Modes

Data-Grist supports three display modes:

- **GRID**: Tabular data display (default)
- **LIST**: Mobile-friendly list format display
- **CARD**: Card format display

```javascript
// Set mode
gristElement.mode = 'GRID' // or 'LIST', 'CARD'
```

## Configuration (Config)

Data-Grist provides various configuration options.

```javascript
const config = {
  columns: [
    {
      type: 'string',
      name: 'name',
      header: 'Name',
      record: {
        editable: true
      },
      sortable: true,
      width: 150
    }
    // More columns...
  ],
  rows: {
    appendable: true, // Allow adding new records
    editable: true, // Allow editing records
    selectable: {
      // Record selection settings
      multiple: true // Allow multiple selections
    }
  },
  pagination: {
    infinite: false, // Use paging instead of infinite scroll
    page: 1, // Starting page
    limit: 20 // Records per page
  }
  // Additional settings...
}
```

### Column Types

```javascript
// String column
{
  type: 'string',
  name: 'firstName',
  header: 'First Name'
}

// Number column
{
  type: 'number',
  name: 'age',
  header: 'Age',
  record: {
    align: 'right'
  }
}

// Boolean column
{
  type: 'boolean',
  name: 'isActive',
  header: 'Active'
}

// Link column
{
  type: 'link',
  name: 'website',
  header: 'Website',
  record: {
    options: {
      href: (column, record) => record.url,
      target: '_blank'
    }
  }
}

// Gutter column (row number, checkbox, button, etc.)
{
  type: 'gutter',
  gutterName: 'sequence' // 'row-selector', 'button', 'dirty', etc.
}
```

## Fetching Data

Data can be retrieved through the `fetchHandler` function.

```javascript
const fetchHandler = async params => {
  const { page, limit, sorters, filters } = params

  // Fetch data from server
  const response = await fetch(`/api/data?page=${page}&limit=${limit}`)
  const data = await response.json()

  return {
    total: data.total,
    records: data.items
  }
}

// Configure the grist
gristElement.fetchHandler = fetchHandler
```

## Record Manipulation API

### Adding Records

```javascript
// Add record at the end (default)
gristElement.addRecord({
  name: 'John Doe',
  age: 30
})

// Add record at the top
gristElement.addRecordToTop({
  name: 'Jane Smith',
  age: 25
})
```

### Record Selection

```javascript
// Get all selected records
const selectedRecords = gristElement.selected

// Set selection
gristElement.selected = [record1, record2]

// Select records using a selector function
gristElement.select(record => record.age > 30)
```

### Tracking Changed Records

```javascript
// Get modified records
const dirtyRecords = gristElement.dirtyRecords

// Check for changes
gristElement.checkDirties()

// Export changes as patches
const patches = gristElement.exportPatchList()
```

## Events

Data-Grist triggers various events.

```javascript
// Record selection change event
gristElement.addEventListener('select-record-change', e => {
  const { records, added, removed } = e.detail
  console.log('Selected records:', records)
})

// Field change event
gristElement.addEventListener('field-change', e => {
  const { after, before, column, record, row } = e.detail
  console.log('Changed field:', column.name, 'from', before, 'to', after)
})

// Record change event
gristElement.addEventListener('record-change', e => {
  const { before, after, column, row } = e.detail
  console.log('Record changed:', row)
})
```

## Advanced Features

### Column Accumulation Feature (Accumulator)

```javascript
{
  type: 'number',
  name: 'amount',
  header: 'Amount',
  accumulator: 'sum' // 'avg', 'count', 'min', 'max' or custom function
}
```

### Tree Structure Data

```javascript
const config = {
  // ...
  tree: {
    childrenProperty: 'children', // Property name where child nodes are stored
    expanded: true // Initial tree expansion state
  }
}
```

### Grouped Headers

```javascript
const config = {
  columns: [
    // ...
    {
      type: 'string',
      name: 'firstName',
      header: 'First Name',
      group: 'personalInfo'
    },
    {
      type: 'string',
      name: 'lastName',
      header: 'Last Name',
      group: 'personalInfo'
    }
  ],
  // ...
  rows: {
    // ...
    groups: [
      {
        name: 'personalInfo',
        title: 'Personal Information'
      }
    ]
  }
}
```

### User Settings Storage

```javascript
// Configure user settings provider
gristElement.personalConfigProvider = {
  async load() {
    // Load saved settings
    return JSON.parse(localStorage.getItem('userGristConfig'))
  },
  async save(preference) {
    // Save user settings
    localStorage.setItem('userGristConfig', JSON.stringify(preference))
  }
}
```

## Styling

Data-Grist can be styled through CSS variables.

```css
ox-grist {
  --grid-header-background-color: #f5f5f5;
  --grid-record-background-color: white;
  --grid-record-odd-background-color: #f9f9f9;
  --grid-header-color: #333;
  --grid-record-hover-background-color: #e9e9e9;
}
```

## API Reference

### Properties

| Property          | Type     | Description                           |
| ----------------- | -------- | ------------------------------------- |
| `mode`            | string   | Display mode ('GRID', 'LIST', 'CARD') |
| `config`          | object   | Grist configuration object            |
| `data`            | object   | Data to display                       |
| `selectedRecords` | array    | Array of selected records             |
| `explicitFetch`   | boolean  | Enable explicit data fetching         |
| `fetchHandler`    | function | Data fetching function                |
| `fetchOptions`    | object   | Data fetching options                 |
| `filters`         | array    | Array of filters                      |
| `sorters`         | array    | Array of sorters                      |
| `pagination`      | object   | Pagination settings                   |

### Methods

| Method                         | Description                     |
| ------------------------------ | ------------------------------- |
| `fetch(reset)`                 | Fetch data                      |
| `addRecord(record)`            | Add a record                    |
| `addRecordToTop(record)`       | Add a record at the top         |
| `deleteSelectedRecords(dirty)` | Delete selected records         |
| `cloneSelectedRecords()`       | Clone selected records          |
| `checkDirties()`               | Check for modified records      |
| `undo()`                       | Undo last action                |
| `redo()`                       | Redo undone action              |
| `reset()`                      | Reset data                      |
| `commit()`                     | Commit changes                  |
| `select(selector, reset)`      | Select records using a function |
| `exportPatchList(options)`     | Export list of change patches   |
| `exportRecords(options)`       | Export record data              |

## Examples

### Basic Grid

```html
<ox-grist
  .mode=${'GRID'}
  .config=${gridConfig}
  .fetchHandler=${fetchData}
  @record-change=${handleRecordChange}
></ox-grist>
```

### Editable Grid

```html
<ox-grist
  .mode=${'GRID'}
  .config=${{
    columns: [/* column definitions */],
    rows: {
      appendable: true,
      editable: true,
      selectable: { multiple: true }
    }
  }}
  .data=${{ records: initialData }}
></ox-grist>
```

### Add Row to Top Button

```html
<button @click="${() => gristRef.addRecordToTop()}">Add Row to Top</button>
<ox-grist id="my-grist"></ox-grist>

<script>
  const gristRef = document.getElementById('my-grist')
</script>
```

## License

MIT
