CxJS

Column Reordering

Grid columns can be reordered by dragging and dropping column headers.

<div controller={PageController}>
  <div class="flex mb-2">
    <Repeater
      records={computable(m.unusedColumns, (columns) =>
        columns.map((key) => allColumns.find((c) => c.key === key)),
      )}
    >
      <DragSource
        class="bg-gray-100 border border-gray-300 px-2 py-1 text-sm mr-1 cursor-pointer"
        data={{ type: "unused-column", key: m.$record.key }}
      >
        <span text={m.$record.header} />
      </DragSource>
    </Repeater>

    <DropZone
      class="border border-dashed border-gray-300 flex-grow px-2 py-1 text-sm transition-colors"
      onDropTest={(e) => e?.source?.type === "grid-column"}
      overClass="bg-yellow-100"
      onDrop={(e, { store }) => {
        let { key } = e.source.column
        store.update(m.columnOrder, (order) => order.filter((c) => c !== key))
        store.update(m.unusedColumns, (cols) => [...cols, key])
      }}
    >
      Drag column here to remove
    </DropZone>
  </div>

  <Grid
    records={m.records}
    mod="fixed-layout"
    scrollable
    border
    style="height: 250px; margin-bottom: 10px"
    lockColumnWidths
    columnParams={m.columnOrder}
    onGetColumns={(columnOrder) =>
      columnOrder.map((key: string) => allColumns.find((c) => c.key === key))
    }
    onColumnDropTest={(e, instance) =>
      (e.source?.type === "grid-column" &&
        e.source?.gridInstance === instance) ||
      e.source?.data?.type === "unused-column"
    }
    onColumnDrop={(e, { store }) => {
      let key =
        e.source.type === "grid-column"
          ? e.source.column.key
          : e.source.data.key
      let { index } = e.target
      let columnOrder = store.get(m.columnOrder)
      let at = columnOrder.indexOf(key)
      let result = columnOrder.filter((c) => c !== key)
      if (at >= 0 && at < index) index--
      result = insertElement(result, index, key)
      store.set(m.columnOrder, result)
      store.update(m.unusedColumns, (unused) => unused.filter((c) => c !== key))
    }}
  />

  <Button onClick="onResetColumns">Reset Columns</Button>
</div>
Drag column here to remove
Name
Continent
Browser
OS
Visits
Alice JohnsonEuropeChromeWindows45
Bob SmithAsiaFirefoxmacOS23
Carol WhiteNorth AmericaSafarimacOS67
David BrownEuropeChromeLinux12
Eva GreenAsiaEdgeWindows89

Drag column headers to reorder them. Drag a column to the drop zone above to remove it from the grid.

Enabling Draggable Columns

Set draggable: true on each column that can be reordered:

{
  key: "fullName",
  header: "Name",
  field: "fullName",
  draggable: true,
}

Each column should have a unique key so the Grid can correctly track column state.

Dynamic Column Generation

Use columnParams and onGetColumns to dynamically generate columns:

<Grid
  columnParams={m.columnOrder}
  onGetColumns={(columnOrder) =>
    columnOrder.map((key) => allColumns.find((c) => c.key === key))
  }
/>

When columnParams changes, onGetColumns is called to regenerate the column list.

Handling Column Drops

Implement onColumnDropTest and onColumnDrop to handle the drag and drop logic:

<Grid
  onColumnDropTest={(e, instance) =>
    e.source?.type === "grid-column" && e.source?.gridInstance === instance
  }
  onColumnDrop={(e, { store }) => {
    let { key } = e.source.column;
    let { index } = e.target;
    // Update column order in store
  }}
/>

Configuration

PropertyTypeDescription
draggablebooleanEnable dragging for this column.
keystringUnique identifier for the column. Required for reordering.
columnParamsanyParameters passed to onGetColumns. Changes trigger column regeneration.
onGetColumnsfunctionCallback to generate columns. Receives columnParams value.
onColumnDropTestfunctionTest if a drop is valid. Receives drag event and instance.
onColumnDropfunctionHandle column drop. Receives drag event and instance.
lockColumnWidthsbooleanPreserve column widths during reordering.

See also: Column Resizing, Dynamic Columns