{"id":20445829,"url":"https://github.com/rapid7/marionette.carpenter","last_synced_at":"2025-04-13T00:43:57.896Z","repository":{"id":19673726,"uuid":"22927449","full_name":"rapid7/marionette.carpenter","owner":"rapid7","description":"A thing that makes tables","archived":false,"fork":false,"pushed_at":"2025-04-11T15:51:54.000Z","size":2902,"stargazers_count":14,"open_issues_count":16,"forks_count":9,"subscribers_count":99,"default_branch":"master","last_synced_at":"2025-04-13T00:43:52.154Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rapid7.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2014-08-13T18:58:03.000Z","updated_at":"2025-04-11T15:51:58.000Z","dependencies_parsed_at":"2022-08-24T14:07:53.336Z","dependency_job_id":"e1ab74a2-50a5-47ea-9265-36e526d94b8e","html_url":"https://github.com/rapid7/marionette.carpenter","commit_stats":{"total_commits":162,"total_committers":13,"mean_commits":"12.461538461538462","dds":0.5617283950617284,"last_synced_commit":"6d4198432ead1ce76f96b6406b42a27617f8da4c"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fmarionette.carpenter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fmarionette.carpenter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fmarionette.carpenter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fmarionette.carpenter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rapid7","download_url":"https://codeload.github.com/rapid7/marionette.carpenter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248650419,"owners_count":21139672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T10:14:31.450Z","updated_at":"2025-04-13T00:43:57.877Z","avatar_url":"https://github.com/rapid7.png","language":"CoffeeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eMarionette.Carpenter\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg title=\"backbone marionette\" src='https://github.com/rapid7/marionette.carpenter/blob/update-demo-site/site/source/assets/images/logo-black.png?raw=true' style='width:100%;'/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003eIt builds tables.\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca title='Build Status' href=\"https://travis-ci.org/rapid7/marionette.carpenter\"\u003e\n    \u003cimg src='https://img.shields.io/travis/rapid7/marionette.carpenter.svg?branch=master' /\u003e\n  \u003c/a\u003e\n  \u003ca href='https://coveralls.io/r/rapid7/marionette.carpenter'\u003e\n    \u003cimg src='https://img.shields.io/coveralls/rapid7/marionette.carpenter.svg' alt='Coverage Status' /\u003e\n  \u003c/a\u003e\n  \u003ca href='https://david-dm.org/rapid7/marionette.carpenter#info=dependencies\u0026view=table'\u003e\n    \u003cimg src='https://img.shields.io/david/rapid7/marionette.carpenter.svg' /\u003e\n  \u003c/a\u003e\n  \u003ca href='https://david-dm.org/rapid7/marionette.carpenter#info=devDependencies\u0026view=table'\u003e\n    \u003cimg src='https://img.shields.io/david/dev/rapid7/marionette.carpenter.svg' /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## About Carpenter\n\n**Easily represent a Backbone collection as a sortable, paginated table.**\n\nOne of the more common tasks when developing web applications is building tabular representations of data. Carpenter aims to make the process of building robust tables as simple as possible, while giving developers the flexibility to easily extend a table's functionality.\n\n### Features\n\n* Searching\n* Sorting\n* Pagination (for both client and server-side collections)\n* Custom views for table cells\n* Button bar generation\n\n## Installation\n\n### Installing via Bower\n\nThe easiest way to get things rocking and rolling is with [Bower](http://bower.io/):\n\n```console\n$ bower install marionette.carpenter\n```\n\nThat will put everything in place along with all the correct dependencies. Easy ice!\n\n### Using with RequireJS\n\nIf you'd like to use Carpenter with [RequireJS](http://requirejs.org/), the following `requirejs.config` should come in handy:\n\n```coffeescript\nrequirejs.config\n  shim:\n    'backbone':\n      deps: ['underscore', 'jquery']\n      exports: 'Backbone'\n   'backbone.radio':\n      deps: ['backbone']\n    'underscore':\n      exports: '_'\n    'marionette':\n      deps: ['backbone', 'backbone.wreqr', 'backbone.babysitter']\n     exports: 'Marionette'\n    'carpenter':\n      deps: ['cocktail', 'backbone.radio', 'underscore.string', 'jquery-resizable-columns', 'marionette']\n\n```\n\nNote that you will also likely need to [specify a `paths` configuration](http://requirejs.org/docs/api.html#config-paths).\n\n### Installing by Hand\n\nFor an artisanal, hand-crafted, manual installation, you'll need to start by installing the...\n\n#### Dependencies\n\nYou can find an up-to-date list of the libraries required by Carpenter in the [`bower.json` file](https://github.com/rapid7/marionette.carpenter/blob/master/bower.json) under the `dependencies` key. Install these as instructed in each project's `README`.\n\n#### Manual Installation\n\nAfter getting the dependencies in place, move the following files into their proper places in your project:\n\n* [`dist/marionette.carpenter.css`](https://github.com/rapid7/marionette.carpenter/blob/master/dist/marionette.carpenter.css) - The base CSS styles for Carpenter tables.\n* [`dist/marionette.carpenter.js`](https://github.com/rapid7/marionette.carpenter/blob/master/dist/marionette.carpenter.js) - The main Carpenter library.\n\n## Usage\n\nBuilding a table couldn't be simpler:\n\n```coffeescript\nnew Marionette.Carpenter.Controller\n  title: 'Users'\n  region: new Backbone.Marionette.Region el: '#users-table-region'\n  collection: usersCollection # a Backbone collection\n  static: true\n  queryParameters: {\n    param1: paramValue1\n    param2: paramValue2\n  }\n  columns: [\n    { attribute: 'first_name' }\n    { attribute: 'last_name' }     \n    { attribute: 'email' }\n  ]\n```\n\nThe above code creates a new table element at `#users-table-region` with pagination controls and sortable columns. We set the title of the table with `title: 'Users'`, indicate the `region` we want the table rendered to, specify that the collection is to be paginated and sorted client-side with `static: true`, and then specify the attributes to load in the table with an array at `columns`. `queryParameters` optionally provides the ability to pass in additional custom parameters.\n\n### Customizing columns\n\nThe `columns` property is where the action's at when you're looking to specify the data that the table loads. We pass an array of objects, with each object representing a column in the table. At a minimum, we need to specify a model attribute that we wish to display for each column:\n\n```coffeescript\ncolumns: [\n  { attribute: 'title' }\n  { attribute: 'author' }\n]\n```\n\nThis will result in two columns, with \"Title\" and \"Author\" headers, loading the data from the respective attributes in the model. We can customize the column's header `label`, as well:\n\n```coffeescript\ncolumns: [\n  {\n    attribute: 'issueCount'\n    label:     'Issues'\n  }\n]\n```\n\n#### Sortability\n\nBy default, every column is considered sortable. This is easily overridden with the `sortable` property in cases where we want to disallow it:\n\n```coffeescript\ncolumns: [\n  {\n    attribute: 'avatar'\n    sortable: false\n  }\n]\n```\n\nWe can also customize the initial sort direction with `defaultDirection`:\n\n```coffeescript\ncolumns: [\n  {\n    attribute:        'salary'\n    defaultDirection: 'desc'\n  }\n]\n```\n\n\n#### Using a custom empty view\n\nIf you have a table with a collection of size 0, carpenter will render its default empty view. However, you can specify a view of your choosing to render instead.\n\n```coffeescript\nclass MyCustomEmptyView extends Marionette.ItemView\n  template: (data) -\u003e\n    \"\"\"\n    \u003cdiv class=\"custom-empty-view\"\u003e\n      \u003cspan\u003eThis table is empty.\u003cspan\u003e\n    \u003c/div\u003e\n```\n\n```coffeescript\nnew Marionette.Carpenter.Controller\n  title: 'Users'\n  region: new Backbone.Marionette.Region el: '#users-table-region'\n  collection: usersCollection # a Backbone collection\n  static: true\n  columns: [\n    { attribute: 'first_name' }\n    { attribute: 'last_name' }     \n    { attribute: 'email' }\n  ],\n  emptyView: MyCustomEmptyView\n```\n\n##### Registering Handlers for the custom view\nSince the custom view is a Marionette view, you can do things on the onRender and onShow callbacks as you would normally do in a marionette application. For Example:\n\n```coffeescript\nclass MyCustomCellView extends Marionette.ItemView\n  template: (data) -\u003e\n    \"\"\"\n    \u003cdiv class=\"custom-cell-view\"\u003e\n      \u003cspan\u003eThis is a cell #{data.name}\u003cspan\u003e\n    \u003c/div\u003e\n    \"\"\"\n  ui:\n   span : '.custom-cell-view span'\n  events:\n   'hover @ui.span' : onHover\n  onHover: -\u003e\n   console.log(\"Lets do something on hover\")  \n  onRender: -\u003e\n   console.log(\"Lets do something special when we render the view\")\n```\n\n#### Using custom cell views\n\nTime to get fancy! Let's say we want to render something more than boring old text in one of our cells. In this case, we'd like to create [a Foundation progress bar](http://foundation.zurb.com/docs/components/progress_bars.html). We'll start by defining a `Marionette.ItemView` for the cell:\n\n```coffeescript\nclass ProjectProgressCellView extends Marionette.ItemView\n  template: (data) -\u003e\n    \"\"\"\n    \u003cdiv class=\"progress round\"\u003e\n      \u003cspan class=\"meter\" style=\"width: #{ data.percentCompleted }%\"\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \"\"\"\n```\n\t\nWe then reference that view in the relevant column's `view` property:\n\n```coffeescript\ncolumns: [\n  {\n    attribute: 'projectTitle'\n    label: 'title'\n  }\n  {\n    attribute: 'contact'\n  }\n  { \n    attribute: 'percentCompleted'\n    label: 'progress'\n    view: ProjectProgressCellView\n  }\n] \n```\n\nIt's also possible to pass options to the view's `initialize` method with the `viewOpts` property. If our above `ProjectProgressCellView` accepted a `class` option to override the progress bar's CSS class, we could set it like so:\n\n```coffeescript\n  { \n    attribute: 'percentCompleted'\n    label: 'progress'\n    view: ProjectProgressCellView\n    viewOpts:\n      class: 'alert round'\n  }\n```\n\n### Action Buttons\n\nAction buttons are buttons that appear above the table. \n\n#### Click Callback\n\nYou can define a click callback handler that provides you with the state of the current table. \n\n+ selectAllState - [Boolean] - True/False that represents whether or not the select all checkbox is selected.\n+ selectedIds - [Array\u003cInteger\u003e] - An array of row ids used when selectAllState is False representing rows with checkboxes selected\n+ deselectedIds - [Array\u003cInteger\u003e] - An array of row ids used when selectAllState is True representing rows with checkboxes \n+ selectedVisibleCollection - [Backbone.Collection] - A collection representing the current selected row that are visible on the table.\n+ tableCollection - [Backbone.Collection] - A collection representing the current visible rows on the table\n\nYou may also define a \"class\" for the button Dom element as well as a label. The containing wrapper Dom node may also have a class defined.\n \n\n```coffeescript\n     actionButtons = [\n          {\n            label: 'Delete'\n            class: 'delete'\n            activateOn: 'any'\n            click: (selectAllState, selectedIDs, deselectedIDs, selectedVisibleCollection, tableCollection) -\u003e\n              console.log(\"Delete\")\n            containerClass: 'action-button-right-separator'\n          }\n          {\n            label: 'Tag'\n            class: 'tag-edit'\n            activateOn: 'any'\n            click: (selectAllState, selectedIDs, deselectedIDs, selectedVisibleCollection, tableCollection) -\u003e\n              console.log(\"Tag\")\n            containerClass: 'action-button-tag-separator'\n          }\n        ]\n```\n\n#### Enable/Disable Button\nCarpenter has built in enable/disable button functionality. You can specify the following options for the 'activateOn' option\n\nThe button will be enabled if `activateOn` is set to: \n\n+ 'any' - If 1 or many rows are selected.\n\n+ 'many' - If more than 1 row is selected\n\n+ 'one' - If only one row is selected\n\n### Collection parse\n\nAllows user to define a custom parse method for marionette carpenter's custom paginator collection.\n\n```coffeescript\nnew Marionette.Carpenter.Controller\n  title: 'Users'\n  region: new Backbone.Marionette.Region el: '#users-table-region'\n  collection: new Backbone.Collection({\n    parse: (data) -\u003e\n      this.totalRecords = data.__total_records\n      data.items\n  })\n  static: true\n  queryParameters: {\n    param1: paramValue1\n    param2: paramValue2\n  }\n  columns: [\n    { attribute: 'first_name' }\n    { attribute: 'last_name' }\n    { attribute: 'email' }\n  ]\n```\n\n### Collection prefetch\n\nOptional method allowing the user access to the marionette carpenter's custom paginator collection.\n## Controlling Pagination\nYou can control the pagination bar programatically by using the convenience methods available on the collection.\n\n```\ncollection = this.tableController.controller\ncollection.goTo(n)\ncollection.nextPage(options)\ncollection.prevPage(options)\n```\n\n### Convenience methods:\n\n\n* **Collection.goTo( n, options )** - go to a specific page\n* **Collection.nextPage( options )** - go to the next page\n* **Collection.prevPage( options )** - go to the previous page\n\n\n**The collection's methods `.goTo()`, `.nextPage()` and `.prevPage()` are all extension of the original [Backbone Collection.fetch() method](http://documentcloud.github.com/backbone/#Collection-fetch). As so, they all can take the same option object as parameter.\n\nThis option object can use `success` and `error` parameters to pass a function to be executed after server answer.\n\n```javascript\ncollection.goTo(n, {\n  success: function( collection, response ) {\n    // called is server request success\n  },\n  error: function( collection, response ) {\n    // called if server request fail\n  }\n});\n```\n\nTo manage callback, you could also use the [jqXHR](http://api.jquery.com/jQuery.ajax/#jqXHR) returned by these methods to manage callback.\n\n```javascript\ncollection\n  .requestNextPage()\n  .done(function( data, textStatus, jqXHR ) {\n    // called is server request success\n  })\n  .fail(function( data, textStatus, jqXHR ) {\n    // called if server request fail\n  })\n  .always(function( data, textStatus, jqXHR ) {\n    // do something after server request is complete\n  });\n});\n```\n\nIf you'd like to add the incoming models to the current collection, instead of replacing the collection's contents, pass `{update: true, remove: false}` as options to these methods.\n\n```javascript\ncollection.prevPage({ update: true, remove: false });\n```\n\n## Development\n\n### Prerequisites\n* install `nodenv` and use NodeJS 5.2.0\n* install `pyenv` and install Python 2\n* install `grunt-cli@1.0.0` using `npm install -g grunt-cli@1.0.0`\n* install the dependencies of the project: `npm install`\n* install the packages with `bower install`\n\n### Build from source\n\n```console\n$ grunt build\n```\n\n### Run Tests\n\n```console\n$ grunt spec\n```\n\nTo run tests on file change:\n    \n```console\n$ grunt watch\n```\n\n## Additional Resources\n\n### API Documentation\n\nYou can generate docco docs by running the following\n\n```console\nnpm install -g docco\n```\n\n```console\ndocco src/**/*.coffee\n```\n\n### Debugging Specs\n\nYou can easily debug specs without using a remote PhantomJS debugger by running the following grunt task\n\n```\ngrunt spec-debug\n```\n\nIt will generate '_SpecRunner.html' which you can just open up in your browser.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapid7%2Fmarionette.carpenter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frapid7%2Fmarionette.carpenter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapid7%2Fmarionette.carpenter/lists"}