import {
  GridApi,
  GridOptions,
  ICellRendererParams,
  createGrid,
  ColDef,
} from "ag-grid-community";

export class DetailCellRenderer {
  private eGui!: HTMLDivElement;
  private detailGridApi!: GridApi;

  init(params: ICellRendererParams) {
    // Create the main container - no padding or margin for full width
    this.eGui = document.createElement('div');
    this.eGui.classList.add('detail-cell-renderer');
    this.eGui.style.cssText = `
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      background-color: #F9F9FB;
      box-sizing: border-box;
    `;

    // Create grid container - full width, full height, no header
    const gridContainer = document.createElement('div');
    gridContainer.classList.add('detail-grid-container');
    gridContainer.style.cssText = `
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0;
      background-color: #F9F9FB;
      box-sizing: border-box;
    `;

    this.eGui.appendChild(gridContainer);

    // Define columns for the detail grid
    const detailColumnDefs: ColDef[] = [
      { 
        field: "callId", 
        headerName: "Call ID",
        width: 100,
        pinned: 'left'
      },
      { 
        field: "direction", 
        headerName: "Direction",
        width: 120,
        cellStyle: (params) => {
          return params.value === 'Outbound' 
            ? { color: '#28a745', fontWeight: 'bold' }
            : { color: '#dc3545', fontWeight: 'bold' };
        }
      },
      { 
        field: "duration", 
        headerName: "Duration",
        width: 100,
        valueFormatter: (params) => `${params.value}m`
      },
      { 
        field: "subject", 
        headerName: "Subject",
        flex: 1,
        minWidth: 200
      },
      { 
        field: "date", 
        headerName: "Date",
        width: 120,
        valueFormatter: (params) => new Date(params.value).toLocaleDateString()
      },
      { 
        field: "status", 
        headerName: "Status",
        width: 100,
        cellStyle: (params) => {
          const statusColors: { [key: string]: string } = {
            'Completed': '#28a745',
            'Missed': '#dc3545',
            'In Progress': '#ffc107'
          };
          return { 
            color: statusColors[params.value] || '#6c757d',
            fontWeight: 'bold'
          };
        }
      }
    ];

    // Grid options for the detail grid
    const detailGridOptions: GridOptions = {
      columnDefs: detailColumnDefs,
      rowData: this.generateDetailData(params.data),
      defaultColDef: {
        sortable: true,
        filter: true,
        resizable: true,
      },
      suppressHorizontalScroll: false,
      suppressColumnVirtualisation: true,
      headerHeight: 40,
      rowHeight: 35,
      animateRows: true,
      enableRangeSelection: true,
      pagination: true,
      paginationPageSize: 10,
      paginationPageSizeSelector: [5, 10, 20],
      onGridReady: () => {
        // Auto-size columns to fit content
        this.detailGridApi.sizeColumnsToFit();
      }
    };

    // Create the detail grid
    this.detailGridApi = createGrid(gridContainer, detailGridOptions);
  }

  getGui() {
    return this.eGui;
  }

  destroy() {
    if (this.detailGridApi) {
      this.detailGridApi.destroy();
    }
  }

  refresh(): boolean {
    return false;
  }

  // Generate sample detail data based on the parent row
  private generateDetailData(parentData: any) {
    const detailData = [];
    const callCount = parentData.calls || 5;
    
    for (let i = 1; i <= Math.min(callCount, 20); i++) {
      const directions = ['Inbound', 'Outbound'];
      const subjects = [
        'Sales inquiry',
        'Technical support',
        'Billing question',
        'Product demo',
        'Follow-up call',
        'Customer onboarding',
        'Renewal discussion',
        'Feature request'
      ];
      const statuses = ['Completed', 'Missed', 'In Progress'];
      
      detailData.push({
        callId: `${parentData.account}-${i.toString().padStart(3, '0')}`,
        direction: directions[Math.floor(Math.random() * directions.length)],
        duration: Math.floor(Math.random() * 60) + 1,
        subject: subjects[Math.floor(Math.random() * subjects.length)],
        date: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(),
        status: statuses[Math.floor(Math.random() * statuses.length)]
      });
    }
    
    return detailData.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
  }
}
import {
  ClientSideRowModelModule,
  FirstDataRenderedEvent,
  GridApi,
  GridOptions,
  ModuleRegistry,
  RowApiModule,
  ValidationModule,
  createGrid,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  MasterDetailModule,
} from "ag-grid-enterprise";
import { DetailCellRenderer } from "./detailCellRenderer";
import { IAccount } from "./interfaces";

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  MasterDetailModule,
  ColumnMenuModule,
  ContextMenuModule,
  ...(process.env.NODE_ENV !== "production" ? [ValidationModule] : []),
]);

let gridApi: GridApi<IAccount>;

const gridOptions: GridOptions<IAccount> = {
  masterDetail: true,
  detailCellRenderer: DetailCellRenderer,
  detailRowHeight: 300, // Reduced since no header
  detailRowAutoHeight: false, // Disable auto height for consistent layout
  
  // CRITICAL: Remove default detail cell padding
  detailCellRendererParams: {
    suppressCount: true,
    template: '<div class="ag-details-row ag-details-grid"></div>'
  },
  
  columnDefs: [
    // group cell renderer needed for expand / collapse icons
    { 
      field: "name", 
      cellRenderer: "agGroupCellRenderer",
      minWidth: 200,
      flex: 1
    },
    { 
      field: "account",
      minWidth: 150,
      flex: 1
    },
    { 
      field: "calls",
      minWidth: 100,
      width: 120,
      type: 'numericColumn'
    },
    { 
      field: "minutes", 
      valueFormatter: "x.toLocaleString() + 'm'",
      minWidth: 120,
      width: 150,
      type: 'numericColumn'
    },
  ],
  
  defaultColDef: {
    sortable: true,
    filter: true,
    resizable: true,
  },
  
  // Ensure detail grids span full width
  embedFullWidthRows: true,
  
  // Performance optimizations
  suppressColumnVirtualisation: true,
  animateRows: true,
  
  // Row selection
  rowSelection: 'single',
  
  onFirstDataRendered: onFirstDataRendered,
  
  // Handle detail grid events
  onRowClicked: (event) => {
    if (event.node.master) {
      // Toggle expand/collapse on row click
      event.node.setExpanded(!event.node.expanded);
    }
  },
  
  // Styling
  getRowStyle: (params) => {
    return { 
      backgroundColor: '#F9F9FB'
    };
  },
  
  // Header styling
  getRowClass: () => 'custom-row-background',
};

function onFirstDataRendered(params: FirstDataRenderedEvent) {
  // Auto-size all columns
  params.api.sizeColumnsToFit();
  
  // Expand first row for demo
  params.api.forEachNode(function (node) {
    node.setExpanded(node.id === "1");
  });
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/master-detail-data.json")
  .then((response) => response.json())
  .then((data: IAccount[]) => {
    gridApi!.setGridOption("rowData", data);
  })
  .catch((error) => {
    console.error("Error loading data:", error);
  });
<!doctype html>
<html lang="en">
  <head>
    <title>
      Typescript Example - Master Detail Custom Detail - Simple Custom Detail
    </title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="robots" content="noindex" />
    
    <link
      href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;700&amp;display=swap"
      rel="stylesheet"
    />
    <style>
/* Master Grid Background Styling - High Specificity */
.ag-header {
  background-color: #F9F9FB !important;
  color: #495057 !important;
  font-weight: 600 !important;
  border-bottom: 1px solid #e0e4e7 !important;
}
.ag-header-cell {
  background-color: #F9F9FB !important;
  border-right: 1px solid #e0e4e7 !important;
}

.ag-theme-quartz .ag-header-cell-label,
.ag-theme-alpine .ag-header-cell-label,
.ag-header-cell-label {
  color: #495057 !important;
}

/* Master Grid Row Styling */
.ag-theme-quartz .ag-row,
.ag-theme-alpine .ag-row,
.ag-row {
  background-color: #F9F9FB !important;
}

.ag-theme-quartz .ag-row-even,
.ag-theme-alpine .ag-row-even,
.ag-row-even {
  background-color: #F9F9FB !important;
}

.ag-theme-quartz .ag-row-odd,
.ag-theme-alpine .ag-row-odd,
.ag-row-odd {
  background-color: #F9F9FB !important;
}

/* Master Grid Hover Effects */
.ag-theme-quartz .ag-row:hover,
.ag-theme-alpine .ag-row:hover,
.ag-row:hover {
  background-color: #f0f0f5 !important;
}

/* Master Grid Cell Styling */
.ag-theme-quartz .ag-cell,
.ag-theme-alpine .ag-cell,
.ag-cell {
  background-color: #Ffffff !important;
}

/* Detail Cell Renderer Styles - Full Width Override */
.detail-cell-renderer {
  width: 100% !important;
  box-sizing: border-box !important;
  margin: 0 !important;
  padding: 0 !important;
  background-color: #F9F9FB !important;
}

.detail-grid-container {
  width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  background-color: #F9F9FB !important;
}

/* Override ag-Grid's default detail row padding/margins */
.ag-details-row {
  padding: 0 !important;
  margin: 0 !important;
  background-color: #F9F9FB !important;
}

.ag-details-grid {
  width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  background-color: #F9F9FB !important;
}

/* Remove any default spacing from the detail cell */
.ag-cell-wrapper.ag-row-group-indent-0 {
  padding-left: 0 !important;
}

/* Ensure the detail grid takes full width */
.detail-grid-container .ag-root-wrapper {
  width: 100% !important;
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-root {
  width: 100% !important;
  background-color: #F9F9FB !important;
}

/* Detail Grid Header Styling */
.detail-grid-container .ag-header {
  background-color: #F9F9FB !important;
  color: #495057 !important;
  font-weight: 600 !important;
  border-bottom: 1px solid #e0e4e7 !important;
}

.detail-grid-container .ag-header-cell {
  background-color: #F9F9FB !important;
  border-right: 1px solid #e0e4e7 !important;
}

.detail-grid-container .ag-header-cell-label {
  color: #495057 !important;
}

/* Detail Grid Row Backgrounds */
.detail-grid-container .ag-row-even {
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-row-odd {
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-row {
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-cell {
  background-color: #F9F9FB !important;
}

/* Detail Grid Hover Effects */
.detail-grid-container .ag-row:hover {
  background-color: #f0f0f5 !important;
  transition: background-color 0.2s ease;
}

/* Detail Grid Pagination */
.detail-grid-container .ag-paging-panel {
  border-top: 1px solid #e0e4e7 !important;
  background-color: #F9F9FB !important;
  padding: 8px 12px !important;
}

/* Grid Root Container Background */
.detail-grid-container .ag-root-wrapper-body {
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-body-viewport {
  background-color: #F9F9FB !important;
}

/* Custom scrollbar for the detail grid */
.detail-grid-container .ag-body-viewport::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.detail-grid-container .ag-body-viewport::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}

.detail-grid-container .ag-body-viewport::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}

.detail-grid-container .ag-body-viewport::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .detail-cell-renderer {
    padding: 10px;
  }
  
  .detail-grid-container {
    height: 250px;
  }
}

/* Master detail specific styling */
.ag-details-row {
  padding: 0 !important;
}

.ag-details-row .ag-details-grid {
  width: 100% !important;
}
</style>
    <style media="only screen">
      :root,
      body {
        height: 100%;
        width: 100%;
        margin: 0;
        box-sizing: border-box;
        -webkit-overflow-scrolling: touch;
      }

      html {
        position: absolute;
        top: 0;
        left: 0;
        padding: 0;
        overflow: auto;
        font-family: -apple-system, "system-ui", "Segoe UI", Roboto,
          "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif,
          "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
          "Noto Color Emoji";
      }

      body {
        padding: 16px;
        overflow: auto;
        background-color: transparent;
      }
    </style>
  </head>
  <body>
    <div id="myGrid" style="height: 100%"></div>
    <script>
      (function () {
        const appLocation = "";

        window.__basePath = appLocation;
      })();
    </script>
    <script>
      var appLocation = "";
      var boilerplatePath = "";
      var systemJsMap = {
        "@ag-grid-community/styles":
          "https://cdn.jsdelivr.net/npm/@ag-grid-community/styles@34.0.2",
        "ag-grid-community":
          "https://cdn.jsdelivr.net/npm/ag-grid-community@34.0.2",
        "ag-grid-enterprise":
          "https://cdn.jsdelivr.net/npm/ag-grid-enterprise@34.0.2/",
      };
      var systemJsPaths = {
        "@ag-grid-community/locale":
          "https://cdn.jsdelivr.net/npm/@ag-grid-community/locale@34.0.2/dist/package/main.cjs.js",
        "ag-charts-community":
          "https://cdn.jsdelivr.net/npm/ag-charts-community@12.0.2/dist/package/main.cjs.js",
        "ag-charts-core":
          "https://cdn.jsdelivr.net/npm/ag-charts-core@12.0.2/dist/package/main.cjs.js",
        "ag-charts-enterprise":
          "https://cdn.jsdelivr.net/npm/ag-charts-enterprise@12.0.2/dist/package/main.cjs.js",
        "ag-charts-types":
          "https://cdn.jsdelivr.net/npm/ag-charts-types@12.0.2/",
      };
    </script>
    <script src="https://cdn.jsdelivr.net/npm/systemjs@0.19.47/dist/system.js"></script>
    <script src="systemjs.config.js"></script>
    <script>
      System.import("main.ts").catch(function (err) {
        document.body.innerHTML =
          '<div class="example-error" style="background:#fdb022;padding:1rem;">' +
          "Example Error: " +
          err +
          "</div>";
        console.error(err);
      });
    </script>
  </body>
</html>

export interface ICallRecord {
    name: string;
    callId: number;
    duration: number;
    switchCode: string;
    direction: string;
    number: string;
}

export interface IAccount {
    name: string;
    account: number;
    calls: number;
    minutes: number;
    callRecords: ICallRecord[];
}
/* Target only the main grid container */
#myGrid .ag-header,
#myGrid .ag-header-cell {
  background-color: #F9F9FB !important;
  color: #495057 !important;
  border-right: 1px solid #e0e4e7 !important;
}

#myGrid .ag-header {
  border-bottom: 1px solid #e0e4e7 !important;
}

#myGrid .ag-header-cell-label {
  color: #495057 !important;
}

/* Master Grid Row Styling - only for main grid */
#myGrid .ag-row,
#myGrid .ag-row-even,
#myGrid .ag-row-odd {
  background-color: #F9F9FB !important;
}

#myGrid .ag-row:hover {
  background-color: #f0f0f5 !important;
}

#myGrid .ag-cell {
  background-color: #F9F9FB !important;
}

/* Detail Cell Renderer Styles - Full Width Override */
.detail-cell-renderer {
  width: 100% !important;
  box-sizing: border-box !important;
  margin: 0 !important;
  padding: 0 !important;
  background-color: #F9F9FB !important;
}

.detail-grid-container {
  width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  background-color: #F9F9FB !important;
}

/* Override ag-Grid's default detail row padding/margins */
#myGrid .ag-details-row {
  padding: 0 !important;
  margin: 0 !important;
  background-color: #F9F9FB !important;
}

#myGrid .ag-details-grid {
  width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  background-color: #F9F9FB !important;
}

/* Remove any default spacing from the detail cell */
#myGrid .ag-cell-wrapper.ag-row-group-indent-0 {
  padding-left: 0 !important;
}

/* Ensure the detail grid takes full width */
.detail-grid-container .ag-root-wrapper {
  width: 100% !important;
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-root {
  width: 100% !important;
  background-color: #F9F9FB !important;
}

/* Detail Grid Header Styling */
.detail-grid-container .ag-header {
  background-color: #F9F9FB !important;
  color: #495057 !important;
  font-weight: 600 !important;
  border-bottom: 1px solid #e0e4e7 !important;
}

.detail-grid-container .ag-header-cell {
  background-color: #F9F9FB !important;
  border-right: 1px solid #e0e4e7 !important;
}

.detail-grid-container .ag-header-cell-label {
  color: #495057 !important;
}

/* Detail Grid Row Backgrounds */
.detail-grid-container .ag-row-even,
.detail-grid-container .ag-row-odd,
.detail-grid-container .ag-row {
  background-color: #F9F9FB !important;
}

.detail-grid-container .ag-cell {
  background-color: #F9F9FB !important;
}

/* Detail Grid Hover Effects */
.detail-grid-container .ag-row:hover {
  background-color: #f0f0f5 !important;
  transition: background-color 0.2s ease;
}

/* Detail Grid Pagination */
.detail-grid-container .ag-paging-panel {
  border-top: 1px solid #e0e4e7 !important;
  background-color: #F9F9FB !important;
  padding: 8px 12px !important;
}

/* Grid Root Container Background */
.detail-grid-container .ag-root-wrapper-body,
.detail-grid-container .ag-body-viewport {
  background-color: #F9F9FB !important;
}

/* Custom scrollbar for the detail grid */
.detail-grid-container .ag-body-viewport::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.detail-grid-container .ag-body-viewport::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}

.detail-grid-container .ag-body-viewport::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}

.detail-grid-container .ag-body-viewport::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .detail-cell-renderer {
    padding: 10px;
  }
  
  .detail-grid-container {
    height: 250px;
  }
}
(function (global) {
    process = { env: { NODE_ENV: 'development' } };
    System.config({
        // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
        transpiler: 'ts',
        typescriptOptions: {},
        meta: {
            typescript: {
                exports: 'ts',
            },
            '*.css': { loader: 'css' },
        },
        paths: {
            // paths serve as alias
            'npm:': 'https://cdn.jsdelivr.net/npm/',
            ...systemJsPaths,
        },
        // map tells the System loader where to look for things
        map: {
            css: (boilerplatePath.length === 0 ? `./` : `${boilerplatePath}/`) + 'css.js',

            ts: 'npm:plugin-typescript@8.0.0/lib/plugin.js',
            tslib: 'npm:tslib@2.3.1/tslib.js',
            typescript: 'npm:typescript@5.4.5/lib/typescript.min.js',

            // appLocation comes from index.html
            app: appLocation,
            ...systemJsMap,
        },
        // packages tells the System loader how to load when no filename and/or no extension
        packages: {
            css: {},
            app: {
                main: './main.ts',
                defaultExtension: 'ts',
            },
            'ag-grid-community': {
                main: './dist/package/main.cjs.js',
                defaultExtension: 'js',
                format: 'cjs',
            },
            'ag-grid-enterprise': {
                main: './dist/package/main.cjs.js',
                defaultExtension: 'js',
                format: 'cjs',
            },
            'ag-charts-types': {
                defaultExtension: 'js',
                format: 'cjs',
            },
            'ag-charts-core': {
                defaultExtension: 'js',
                format: 'cjs',
            },
            'ag-charts-community': {
                defaultExtension: 'js',
                format: 'cjs',
            },
            'ag-charts-enterprise': {
                defaultExtension: 'js',
                format: 'cjs',
            },
            '@ag-grid-community/locale': {
                format: 'cjs',
            },
        },
    });
})(this);

window.addEventListener('error', (e) => {
    console.error('ERROR', e.message, e.filename);
});
{
  "name": "ag-grid-example",
  "dependencies": {
    "ag-grid-community": "34.0.2",
    "ag-grid-enterprise": "34.0.2"
  },
  "devDependencies": {
    "@types/node": "^22"
  }
}