cloudflarearchive / backgrid

Finally, an easily stylable semantic HTML data grid widget with a Javascript API that doesn't suck.
http://backgridjs.com
MIT License
2.01k stars 324 forks source link

Allow the use of custom header rows #335

Open machineghost opened 11 years ago

machineghost commented 11 years ago

Currently Backgrid is very good about letting you override its classes with your own ... except when it comes to HeaderRow. While the rest of Backgrid does something like:

this.row = options.row || Row;

Backgrid.Header instead does:

this.row = new Backgrid.HeaderRow({

Would it be possible to change the code to instead be something like:

var HeaderRow = options.row || Backgrid.HeaderRow;
this.row = new HeaderRow({

?

wyuenho commented 11 years ago

Mmmm. Under what circumstances would you need a different header row? The only reason a HeaderRow is there is because the makeCell method needs to be a little bit different in order to get the right HeaderCell class.

machineghost commented 11 years ago

So, our designer replaced the normal contents of the first two THs in a grid with the filter control. This required having a colspan="2" on the first TH and skipping the second TH, and it seemed like the best (only?) way to implement something like that was with a custom header row.

kriswill commented 11 years ago

You could style it to do what you want. For instance:

.my-grid .filter-column-header { position: relative; }
.my-grid .filter { position: absolute; top: 0; left: 0; width: 200px; }

Then render your filter interface in that column, with class='filter'. It should be out of the flow of the TH and resting on top of the table wherever you position it.

machineghost commented 11 years ago

Thanks for the suggestion kriswill, but I'm afraid I'm not quite following it. Is your suggestion basically that I render the table as normal, with header text and such in the first two THs, then use CSS to overlay the filter on top of the the first two THs? If so, that suggestion seems problematic. That sort of thing can cause brief (but unprofessional looking) flashes of the real table underneath. Also, it would seem to work only if all of the columns involved had a fixed width (which isn't the case for me unfortunately).

Or, if I misunderstood your suggestion, could you please clarify?

kriswill commented 11 years ago

From your description it sounded like you want the left-most TH cell using COLSPAN="2" so that you can consume 2-columns worth of space for a filter UI. With that assumption, given the fact that Backgrid renders table header cells that are 1-to-1 with the columns definition, it will not be possible to cleanly obtain a colspan 2. My suggestion was a way to get around that limitation by leveraging HTML/CSS with absolute positioning and dodging the table layout rules all together. However, if you have a tricky fluid-width table, that may not work for you.

wyuenho commented 11 years ago

I don't think tampering with CSS is a sufficient solution for the need to support colspan. There are many use cases that demand colspan.

I think this issue can be folded into a larger issue of whether we should and how to support the rest of the table elements and attributes. I suppose <caption> could be easily supported. I'm not sure how rowspan and colspan should be supported. But let's tackle this one at a time. Suppose we were to support colspan first, what would the ideal implementation look like? I'm just going to throw some idea out and see what you guys think.

Since supporting colspan presupposes that the column numbers will not be same for every row, which happens to go beyond the current Backgrid assumption. We'll need a way to define which columns for which rows should have what spanning column groups, and what the cells inside those group should be.

rowspan could be supported the same way. I suppose.

Here's one configuration format I can think of:

var grid: Backgrid.Grid({
  columns: [{
     ...
  }],
  ...,
  colSpanGroups: {
    // every 4th column starting from 1, accepts what :nth-child accepts,
    // linear equation `an+b` and `odd` and `even`, can target pretty much anything
    "3n+1": {
       span: 2, // how many consecutive columns, including the matched column should use this definition
       headerCell: SomeHeaderCell, // required if group key matches header rows
       cell: SomeBodyCell, // required if group key matches body rows
       formatter: SomeBodyCellFormatter, // optional
       name: "attributename", // can be an empty string like how select-all works
       label: "Label", // can be an empty string like how select-all works
       editable: true
       // renderable? sortable and friends?
     },
     ... // more of the same
  }
});

The main risk is I don't know whether we should or how to support renderable, sortable and friends. Would love to hear your ideas here.

wyuenho commented 11 years ago

As a side note, @machineghost you can implement your own header now by turning off the default header. This is a new feature in master.

machineghost commented 11 years ago

Thanks @wyuenho! Right now I am making my header row by setting this.header in my grid's initialize. That works, but I have to copy/paste the whole initialize method of Header in my header sub-class, so I can't wait to DRY my code out with the new stuff in master :-)

As for the complications, my unscripted thoughts are:

renderable: It seems like un-renderable columns should just be ignored for the purposes of calculating colspans

sortable: One approach would just be to say that a secondarily colspaned cell would count as having no values. That might be good for v1, and then a future improvement could add a way to define how colspanned cells should be sorted to the Column's definition (although the implementation of that might be tricky, as I'd think you'd want access to the all of the span-ed cell's data to do the sorting).

syntax: It seems to me there are too potential use cases for colspans: A) I've got a grid with a bunch of data, and I want my colspans to be determined by that data, or B) I've got a grid with a bunch of data, but I don't care what that data is I just want arbitrary colspans on arbitrary rows

Your current syntax seems to cater more to the B) case, and it allows one to specify any colspan they want, which is an obvious plus. However, it would seem more BackGrid-ish to instead define colspans on the column view. This way you could say something like "if I get an object with a foo property equal to 'bar' in my cell's data, make that cell span two columns".

I'd argue that an A) syntax makes more sense for Backgrid, because if someone just wants "make x5, y7 colspan=4", ie. a B) syntax, they can easily apply implement it as a post-render action on the table; you don't need to know what's in the table (heck that table doesn't even need to be a BackGrid) to do that. But if you want to actually make your colspans based on data, and Backgrid only provides an arbritrary mechanism for colspans, you're out of luck.

kriswill commented 11 years ago

My only concern about using colspan deep in the belly of the column layouts, is that it will make draggable/reorderable columns very difficult to implement. Perhaps, you would have to make them mutually exclusive features.

wyuenho commented 11 years ago

@machineghost

  1. Right, dealing with an unrenderable column is easy, even with the presence of colspangroups, what I mean is, what is an unrenderable colspangroup?
  2. I guess semantically it'll make sense to say a colspan'ed column has the same data for 2 different columns for the purpose of sorting, arbitrarily picking a column and decide it has no value seems quite inconsistent.
  3. Someone once asked if Backgrid can be used to build a TV guide, I guess this would fall under A).

@kriswill

Yes making draggable/reorderable columns will be tricky with colspans. But this feature will probably be in an extension anyway. Just need to note in the docs that the user shouldn't drop it in a grid with colspans.

machineghost commented 11 years ago

I'm not clear on what an unrenderable colspangroup would be either ... do colspangroups have to be potentially unrenderable?

The TV guide thing seems like a perfect instance of data-based colspans. But I honestly don't know if an "A)" syntax is right for BackGrid; I'm just saying that it's more likely to be useful for me personally in the future, and it lets people do something they can't do already (vs. a "B)" syntax which people can currently simulate without any changes to BackGrid).

wyuenho commented 11 years ago

After some careful consideration, I think a colspangroup could be unrenderable. In that case the unrenderable spanning cells can simply be blank.

I don't think a column that contains spanning cells should be sortable at all, but other columns that has no spanning cells should still be sortable.

@machineghost To support both your use cases, I can simply make the span attribute to take a function or a value. You can calculate the span depending on whatever you want inside the function.

machineghost commented 11 years ago

That sounds good, but I'm a little unclear about how this would work. Let's say I want a custom header cell that spans two columns; if I did:

colSpanGroups: {
    0": {
       span: 2,
       headerCell: MyHeaderCell,

would that affect all of my rows, or would Backgrid somehow know that I only want to affect header cells (since I didn't include a cell option)?

hugohenrique commented 10 years ago

any news?

WRidder commented 10 years ago

At the moment we (the company I'm involved with) are looking into developing several features for Backgrid which we would like to contribute back to the community. We will be looking at reorderable columns, grouped columns and resizeable columns. We would like to do this in a constructive manner with the rest of the community such that it has the most chance of being useful to the Backgrid community.

Regarding grouped columns specifically, we'll join this discussion and submit ideas/suggestions asap.

WRidder commented 10 years ago

This is a proposal which I'd like to discuss with anyone interested. The proposal does not yet contain full documentation and tests.

The overall intent is to make it fit with backgrid in a manner which requires only minor changes to core backgrid and can easily be enabled/disabled. It has no additional dependencies.

Grouped columns

Architecture

It's created as an extension to the core Backgrid.Header object. It uses a seperate column layout definition to determine the structure of the header. Essentialy it determines how many rows should be present in the header and creates standard Backgrid.HeaderRow objects for every row using a calculated column definition. Parent elements are currently not sortable.

The column definition is traversed to determine the structure. Any objects in this definition which has no children is assumed to have a column definition present in the standard columns collection.

Usage

// Grouped column definition
var colLayout = [
  ...
  {
    name: "Balance sheet",
    children: [
      {
        name: "Revenues",
        children: [
          {
            name: "domestic"
          },
          {
            name: "exports"
          },
          {
            name: "total"
          }
        ]
      },
      {
        name: "expenditure"
      },
      {
        name: "profits"
      }
    ]
  },
 ...
];

var groupedHeader = Backgrid.Extension.groupedHeader.extend({
  columnLayout: colLayout
});
var pageableGrid = new Backgrid.Grid({
  header: groupedHeader,
  columns: columns,
  collection: pageableTerritories
});

Discussion

I'd love to hear anyone's input on this extension.

Update (12-06-14)

Example bottom-up hierarchy:

// Example (same balance sheet structure as above):
var columnDef = [
...
{
  ...
  name: "domestic",
  nesting: ["Revenues", "Balance sheet"]
  ...
},
{
  ...
  name: "exports",
  nesting: ["Revenues", "Balance sheet"]
  ...
},
{
  ...
  name: "total",
  nesting: ["Revenues", "Balance sheet"]
  ...
},
{
  ...
  name: "expenditure",
  nesting: ["Balance sheet"]
  ...
},
{
  ...
  name: "profits",
  nesting: ["Balance sheet"]
  ...
}
...
];

Demo: http://techwuppet.com/backgrid_poc_demo/ Repo: https://github.com/WRidder/backgrid-grouped-columns/