From dfd3a801b8fe242a1bde1f9ec3c1d66a2bec5957 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 27 May 2014 16:37:06 -0400 Subject: [PATCH] Move documentation from the GH wiki into the repository Based on @edhager's work of similar nature for 0.4 --- CHANGES.md | 7 +- README.md | 10 +- doc/README.md | 51 +++ doc/components/column-plugins/editor.md | 118 ++++++ doc/components/column-plugins/selector.md | 39 ++ doc/components/column-plugins/tree.md | 75 ++++ doc/components/core-components/Grid.md | 192 +++++++++ .../core-components/GridFromHtml.md | 79 ++++ doc/components/core-components/List.md | 77 ++++ .../OnDemandList-and-OnDemandGrid.md | 126 ++++++ doc/components/extensions/ColumnHider.md | 52 +++ doc/components/extensions/ColumnReorder.md | 47 +++ doc/components/extensions/ColumnResizer.md | 62 +++ doc/components/extensions/CompoundColumns.md | 54 +++ doc/components/extensions/DijitRegistry.md | 22 + doc/components/extensions/DnD.md | 74 ++++ doc/components/extensions/Pagination.md | 74 ++++ doc/components/mixins/CellSelection.md | 27 ++ doc/components/mixins/ColumnSet.md | 56 +++ doc/components/mixins/Keyboard.md | 114 +++++ doc/components/mixins/Selection.md | 108 +++++ doc/components/utilities/misc.md | 47 +++ doc/components/utilities/mouse.md | 36 ++ doc/components/utilities/touch.md | 27 ++ doc/migrating/API-Comparison.md | 279 +++++++++++++ doc/migrating/Usage-Comparison.md | 389 ++++++++++++++++++ doc/usage/Limitations.md | 68 +++ doc/usage/Styling-dgrid.md | 112 +++++ doc/usage/Working-with-Events.md | 74 ++++ doc/usage/Working-with-Widgets.md | 40 ++ 30 files changed, 2528 insertions(+), 8 deletions(-) create mode 100644 doc/README.md create mode 100644 doc/components/column-plugins/editor.md create mode 100644 doc/components/column-plugins/selector.md create mode 100644 doc/components/column-plugins/tree.md create mode 100644 doc/components/core-components/Grid.md create mode 100644 doc/components/core-components/GridFromHtml.md create mode 100644 doc/components/core-components/List.md create mode 100644 doc/components/core-components/OnDemandList-and-OnDemandGrid.md create mode 100644 doc/components/extensions/ColumnHider.md create mode 100644 doc/components/extensions/ColumnReorder.md create mode 100644 doc/components/extensions/ColumnResizer.md create mode 100644 doc/components/extensions/CompoundColumns.md create mode 100644 doc/components/extensions/DijitRegistry.md create mode 100644 doc/components/extensions/DnD.md create mode 100644 doc/components/extensions/Pagination.md create mode 100644 doc/components/mixins/CellSelection.md create mode 100644 doc/components/mixins/ColumnSet.md create mode 100644 doc/components/mixins/Keyboard.md create mode 100644 doc/components/mixins/Selection.md create mode 100644 doc/components/utilities/misc.md create mode 100644 doc/components/utilities/mouse.md create mode 100644 doc/components/utilities/touch.md create mode 100644 doc/migrating/API-Comparison.md create mode 100644 doc/migrating/Usage-Comparison.md create mode 100644 doc/usage/Limitations.md create mode 100644 doc/usage/Styling-dgrid.md create mode 100644 doc/usage/Working-with-Events.md create mode 100644 doc/usage/Working-with-Widgets.md diff --git a/CHANGES.md b/CHANGES.md index f2b960be5..2a58bade7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ -This document outlines changes since 0.3.0. For older changelogs, see the -[dgrid wiki](https://github.com/SitePen/dgrid/wiki). +**Note:** Going forward, release notes will be maintained on the GitHub repository's +[releases page](https://github.com/SitePen/dgrid/releases). This file will likely be +removed in the future. # master (0.3.15-dev) @@ -7,6 +8,8 @@ This document outlines changes since 0.3.0. For older changelogs, see the ### General/Core +* Documentation has been moved out of the wiki and into the repository under the + doc folder, to facilitate maintaining relevant documentation for both 0.3 and 0.4. * Fixed a regression in `List` which could cause errors in certain situations due to a feature test being performed before the document is ready. (#907) diff --git a/README.md b/README.md index e0b4f00aa..55db34bc6 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,11 @@ on APIs new to 1.8 or 1.9, so as to maintain compatibility with 1.7. # Documentation -Documentation for dgrid components is available in the -[dgrid GitHub project wiki](https://github.com/SitePen/dgrid/wiki). -The wiki's content may still be obtained for offline reading by cloning -the wiki repository, as indicated under the "Git Access" tab. +The [doc folder](./doc) contains documentation for dgrid components. +In addition, the website hosts a number of [tutorials](http://dgrid.io/#tutorials). -In addition to the documentation on the wiki, if upgrading from a previous -dgrid release, please be sure to read the changelog, found in CHANGES.md. +If upgrading from a previous dgrid release, please be sure to read the +[release notes](https://github.com/SitePen/dgrid/releases). # Testing diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 000000000..2421da752 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,51 @@ +# dgrid Documentation + +This folder contains the official dgrid documentation. It includes +information on dgrid's core components, as well as information on various +mixins, plugins, and extensions that are available to further expand dgrid's +default functionality. + +## Components + +* Core Components + * [List](components/core-components/List.md) + * [Grid](components/core-components/Grid.md) + * [GridFromHtml](components/core-components/GridFromHtml.md) + * [OnDemandList and OnDemandGrid](components/core-components/OnDemandList-and-OnDemandGrid.md) +* Mixins + * [Keyboard](components/mixins/Keyboard.md) + * [ColumnSet](components/mixins/ColumnSet.md) + * [Selection](components/mixins/Selection.md) + * [CellSelection](components/mixins/CellSelection.md) +* Column Plugins + * [editor](components/column-plugins/editor.md) + * [tree](components/column-plugins/tree.md) + * [selector](components/column-plugins/selector.md) +* Extensions + * [ColumnReorder](components/extensions/ColumnReorder.md) + * [ColumnResizer](components/extensions/ColumnResizer.md) + * [ColumnHider](components/extensions/ColumnHider.md) + * [CompoundColumns](components/extensions/CompoundColumns.md) + * [Pagination](components/extensions/Pagination.md) + * [DijitRegistry](components/extensions/DijitRegistry.md) + * [DnD](components/extensions/DnD.md) +* Utilities + * [mouse](components/utilities/mouse.md) + * [touch](components/utilities/touch.md) + * [misc](components/utilities/misc.md) + +## Usage + +* [Working with Events](usage/Working-with-Events.md) +* [Working with Widgets](usage/Working-with-Widgets.md) +* [Styling dgrid](usage/Styling-dgrid.md) +* [Limitations](usage/Limitations.md) + +## Migrating from dojox/grid + +* [API Comparison](migrating/API-Comparison.md) +* [Usage Comparison](migrating/Usage-Comparison.md) + +## Tutorials + +Various tutorials are available on [dgrid.io](http://dojofoundation.org/packages/dgrid/#tutorials). \ No newline at end of file diff --git a/doc/components/column-plugins/editor.md b/doc/components/column-plugins/editor.md new file mode 100644 index 000000000..924a9cce9 --- /dev/null +++ b/doc/components/column-plugins/editor.md @@ -0,0 +1,118 @@ +# editor + +The editor plugin provides the ability to render editor controls within cells +for a column. When used in conjunction with a store-backed grid such as an +[OnDemandGrid](../core-components/OnDemandList-and-OnDemandGrid.md#ondemandgrid), edited fields are directly +correlated with the dirty state of the grid; changes can then be saved back to +the store based on edits performed in the grid. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid","dgrid/editor", "dgrid/Keyboard", "dgrid/Selection" +], function(declare, OnDemandGrid, editor, Keyboard, Selection){ + var editGrid = new (declare([OnDemandGrid, Keyboard, Selection]))({ + store: myStore, + columns: [ + editor({ + label: "Name", + field: "name", + editor: "text", + editOn: "dblclick" + }), + // ... + ] + }, "editGrid"); +}); +``` + +For more examples of editor in use, see the various `editor` test pages, as well +as the `GridFromHtml_editors` test page for declarative examples. + +## Additional Column Definition Properties + +The editor plugin supports the following additional column definition properties. + +Property | Description +-------- | ----------- +`editor` | The type of component to use for editors in this column; either a string specifying a type of standard HTML input to create, or a Dijit widget constructor to instantiate. +`editOn` | A string containing the event (or multiple events, comma-separated) upon which the editor should activate. If unspecified, editors are always displayed in this column's cells. +`editorArgs` | An object containing input attributes or widget arguments. For HTML inputs, the object will have its key/value pairs applied as node attributes via `put-selector`; for widgets, the object will be passed to the widget constructor. +`canEdit(object, value)` | A function returning a boolean value indicating whether or not the cell for this column should be editable in a particular row. Receives the item for the current row, and the value to be rendered (i.e. the return from the column's `get` function if any, or the value of the `field` specified in the column). +`autoSave` |If `true`, the grid's `save` method will be called as soon as a change is detected in an editor in this column. Defaults to `false`. **Note:** if an error is encountered as a result of a store operation triggered by `autoSave`, a `dgrid-error` event will be emitted. +`dismissOnEnter` | By default, pressing enter will store the current value in the grid's dirty data hash. This can be undesirable particularly for textarea editors; setting this property to `false` will disable the behavior. + +For convenience, the `editor` and `editOn` properties may also be specified as +the second and third arguments to the `editor` function. For example, both of +the following would result in an editor which presents a text field when a cell +in the column is double-clicked: + +```js +// long version, everything in column def object +editor({ + editor: "text", + editOn: "dblclick", + /* rest of column definition here... */ +}) + +// shorthand version, editor and editOn as arguments +editor({ /* rest of column definition here... */ }, "text", "dblclick") +``` + +## Additional Grid APIs + +When the editor plugin is applied to a column in a grid, the grid is augmented with +the following method. + +Method | Description +------ | ----------- +`edit(cell)` | Activates/focuses the specified cell (which can be a cell object from `grid.cell()`, or a DOM node or event resolvable to one). + +## Events + +The editor plugin emits a `dgrid-datachange` event whenever an editor field +loses focus after its value is changed. This event includes the following +properties: + +* `grid`: The Grid instance in which the edit occurred +* `cell`: The `cell` object to which the edit applied, as reported by the + `grid.cell` method +* `oldValue`: The value before the edit occurred +* `value`: The value after the edit occurred + +This event bubbles and is cancelable; if the event is canceled, the modification +will be reverted. + +Editors with `editOn` set also emit `dgrid-editor-show` and `dgrid-editor-hide` +events when the editor is shown and hidden, respectively. These events also +include `grid` and `cell` properties and bubble; however, they are not +cancelable. If an editor should not be shown under specific circumstances, +include a `canEdit` function in the column definition. + +## Recommendations for the editOn property + +If attempting to trigger an editor on focus (to accommodate keyboard and mouse), +it is highly recommended to use dgrid's custom event, `dgrid-cellfocusin` +instead of `focus`, to avoid confusion of events. Note that this requires also +mixing the Keyboard module into the Grid constructor. + +If touch input is a concern for activating editors, the easiest solution is to +use the `click` event, which browsers on touch devices tend to normalize to fire +on taps as well. If a different event is desired for desktop browsers, it is +possible to do something like the following: + +```js +require( + ["dgrid/OnDemandGrid", "dgrid/editor", "dojo/has" /*, ... */], + function(OnDemandGrid, editor, has /*, ... */){ + var columns = [ + /* ... more columns here ... */ + editor({ name: "name", label: "Editable Name" }, "text", + has("touch") ? "click" : "dblclick") + ]; + /* ... create grid here ... */ + } +); +``` + +There are also a couple of useful simple gesture implementations available in +the `util/touch` module, namely `tap` and `dbltap`. \ No newline at end of file diff --git a/doc/components/column-plugins/selector.md b/doc/components/column-plugins/selector.md new file mode 100644 index 000000000..23dc428dd --- /dev/null +++ b/doc/components/column-plugins/selector.md @@ -0,0 +1,39 @@ +# selector + +Used in conjunction with the [Selection](../mixins/Selection.md) mixin, the selector plugin dedicates +a column to the purpose of rendering a selector component, providing alternate +means for selecting and deselecting rows in a grid. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/Selection", "dgrid/selector" +], function(declare, OnDemandGrid, Selection, selector){ + var grid = new (declare([OnDemandGrid, Selection]))({ + store: myStore, + selectionMode: "single", + columns: { + col1: selector({ label: "Select", selectorType: "radio" }), + col2: "Column 2" + } + }, "grid"); + // ... +}); +``` + +## Usage + +A selector column can be used to allow selection even in a grid where +`selectionMode` is set to `none`, in which case the controls in the selector +column will be the only means by which a user may select or deselect rows. + +## Additional Column Definition Properties + +The selector plugin supports the following additional column definition properties. + +Property | Description +-------- | ----------- +`disabled(item)` | A function which, provided the item for a particular row, should return `true` if the selector control should be disabled for that row. Returning `true` will also prevent the row in question from being directly selected via the [Selection](../mixins/Selection.md) mixin. +`selectorType` | Specifies the type of selector component to use. Defaults to `"checkbox"`, but `"radio"` may also be specified, as a more appropriate choice for grids in single-selection mode. + +Alternatively, `selectorType` may be specified as the second argument to the +`selector` function instead of including it within the column definition. \ No newline at end of file diff --git a/doc/components/column-plugins/tree.md b/doc/components/column-plugins/tree.md new file mode 100644 index 000000000..a99412772 --- /dev/null +++ b/doc/components/column-plugins/tree.md @@ -0,0 +1,75 @@ +# tree + +The tree plugin enables expansion of rows to display children. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/tree", "dgrid/Keyboard", "dgrid/Selection" +], function(declare, OnDemandGrid, tree, Keyboard, Selection){ + var treeGrid = new (declare([OnDemandGrid, Keyboard, Selection]))({ + store: myStore, + columns: [ + tree({ label: "Name", field: "name" }), + { label:"Type", field: "type", sortable: false}, + { label:"Population", field: "population" }, + { label:"Timezone", field: "timezone" } + ] + }, "treeGrid"); +}); +``` + +## Store Considerations + +The tree plugin expects to operate on a store-backed grid, such as an +[OnDemandGrid](../core-components/OnDemandList-and-OnDemandGrid.md#ondemandgrid) or a grid with the [Pagination](../extensions/Pagination.md) +extension mixed in. + +The store connected to the grid is expected to provide a `getChildren(object, options)` +method to return the children for a given item. Note that for best results, +`getChildren` should return results with an `observe` function as well +(like `query`), so that changes to children can also be reflected as they occur. + +When calling `getChildren`, dgrid will include an `originalQuery` property in +the `options` argument, containing the value from the grid's `query` property. +This allows `getChildren` implementations to perform the same filtering on +child levels if desired. + +The following is a simple example of what a `getChildren` implementation could +look like in an extension to `dojo/store/Memory`, where hierarchy is indicated +by a `parent` property on child items: + +```js +getChildren: function(parent, options){ + // Support persisting the original query via options.originalQuery + // so that child levels will filter the same way as the root level + return this.query( + lang.mixin({}, options && options.originalQuery || null, + { parent: parent.id }), + options); +} +``` + +The store may also (optionally) provide a `mayHaveChildren(object)` method which +returns a boolean indicating whether or not the row can be expanded. If this +is not provided, all items will be rendered with expand icons. + +## Additional Column Definition Properties + +The tree plugin supports the following additional column definition properties. + +Property | Description +-------- | ----------- +`shouldExpand(row, level, previouslyExpanded)` | an optional function which returns a boolean indicating whether the given row should be expanded when rendered. The default implementation simply returns the value of `previouslyExpanded`, which denotes whether the row in question was previously expanded before being re-rendered. +`renderExpando()` | an optional function which can be overridden to customize the logic for rendering the expando icon beside each tree cell's content. +`indentWidth` | the number of pixels to indent each nested level of children; the default is `9`. +`collapseOnRefresh` | a boolean indicating whether to collapse all parents (essentially "forgetting" expanded state) whenever the grid is refreshed; the default is `false`. +`allowDuplicates` | If this is set to `true`, a single object (and single id) can be used in multiple places in a tree structure. However, enabling this means that `grid.row(id)` will only return top-level objects (since it can't disambiguate other levels). The default is `false`. + +## Additional Grid APIs + +When the tree plugin is applied to a column in a grid, the grid is augmented with +the following method. + +Method | Description +------ | ----------- +`expand(row, expand)` | Expands or collapses the row indicated by the given Row object (from `grid.row(target)`) or a `dgrid-row` element. The optional `expand` argument specifies whether the row should be expanded (`true`) or collapsed (`false`); if unspecified, the method toggles the current expanded state of the row. Returns a promise which resolves after data for the children has been retrieved. \ No newline at end of file diff --git a/doc/components/core-components/Grid.md b/doc/components/core-components/Grid.md new file mode 100644 index 000000000..20397a174 --- /dev/null +++ b/doc/components/core-components/Grid.md @@ -0,0 +1,192 @@ +# Grid + +Grid extends List to provide tabular display of data items, with different +fields arranged into columns. + +```js +require(["dgrid/Grid"], function(Grid){ + var columns = { + first: { + label: "First Name" + }, + last: { + label: "Last Name" + } + }; + var grid = new Grid({ columns: columns }, "grid"); // attach to a DOM id + grid.renderArray(arrayOfData); // render some data +}); +``` + +## APIs + +In addition to the methods and properties inherited from [List](List.md), the Grid +component also exposes the following properties and methods. + +### Property Summary + +Property | Description +-------- | ----------- +`formatterScope` | Optional object; if specified, column formatters may be specified as strings instead of functions, in which case they will be searched for as properties within the given `formatterScope` object, and executed in the context of that object. + +### Method Summary + +Method | Description +------ | ------------- +`cell(target[, columnId])` | Analogous to the `row` method, but at the `cell` level instead. The `cell` method can look up based on an event or DOM element, or alternatively, a data item (or ID thereof) and the ID of a column. Returns an object containing the following properties: `row` - a Row object (as would be obtained from the `row` method) for the row the cell is within, `column` - the column definition object for the column the cell is within, `element`- the cell's DOM element. +`column(target)` | Returns the column definition object for the given column ID; typically analogous to `cell(...).column`. +`left(cell[, steps])` | Given a cell object (or something that resolves to one via the `cell` method), returns a cell object representing the cell located `steps` cells to the left (where `steps` defaults to `1`), wrapping to previous rows if necessary. +`right(cell[, steps])` | Same as `left()`, but operating towards the right, wrapping to subsequent rows if necessary. +`styleColumn(columnId, css)` | Programmatically adds styles to a column, given its column id, by injecting a rule string into a stylesheet in the document. Returns a handle with a`remove` function, which can be called to later remove the added style rule. Styles added via this method will be removed when the instance is destroyed if `cleanAddedRules` is set to `true`. +`updateSortArrow(sort, updateSort)` | Updates the placement of the sort arrow indicator in the appropriate header cell. Normally called automatically, but can be called manually in the case of custom sort logic where the `dgrid-sort` event is canceled. `sort` is the new sort order to be reflected by the UI update; `updateSort` is an optional boolean (defaulting to `false`) which, if `true`, will update the internal `_sort` variable to keep it in sync. + +By default, the Grid renders a header, containing cells which display the label +of each column. This can be disabled by setting `showHeader: false` in the +arguments object to the Grid; it can also be changed later using +`set("showHeader", ...)`. + +## Events + +The Grid component emits one custom event, `dgrid-sort`, when a header cell is +clicked to initiate a sort. This event includes the following properties: + +* `grid`: The Grid instance which fired the event +* `parentType`: The original type of event responsible for firing this one + (`click` or `keydown` within a header cell) +* `sort`: An array of objects with `attribute` and optionally `descending` + properties, representing the new sort order to be put into effect + +The `dgrid-sort` event bubbles and is cancelable; if canceled, the sort +order will not be set. This can be useful for instituting custom sort logic +where setting the actual sort on an array or store is undesirable; in this +case, `updateSortArrow` should be called manually if the header is to be +updated. + +## Specifying Grid Columns + +In the simplest cases, the columns of the grid are defined via the `columns` +property. This property can be a hash (object) or array, containing column +definition objects. When `columns` is an object, each property's key represents +the `id` and `field` of the column, and each value is the column definition object. +When `columns` is an array, the numeric indices become the column IDs; `field` +must be specified within each definition. + +Generally, using object notation is slightly more concise and convenient. +However, it's worth noting that doing so relies on the order of enumeration +employed by the JavaScript runtime. Typically this isn't a problem, as it +matches the order in which properties are specified, but one common exception is +in the case of keys coercible to numbers. + +### Columns using an object + +This is an example of a `Grid` using object column definitions: + +```js +require(["dgrid/Grid"], function(Grid){ + var columns = { + first: { + label: "First Name" + }, + last: { + label: "Last Name" + }, + age: { + label: "Age", + get: function(object){ + return (new Date() - object.birthDate) / 31536000000; + } + } + }; + var grid = new Grid({ columns: columns }, "grid"); // attach to a DOM id + grid.renderArray(arrayOfData); // render some data + // ... +}); +``` + +### Columns using an array + +Alternatively, the same columns as above could be defined in an array, as +follows: + +```js + var columns = [ + { + label: "First Name", + field: "first" + }, + { + label: "Last Name", + field: "last" + }, + { + label: "Age", + field: "age", + get: function(object){ + return (new Date() - object.birthDate) / 31536000000; + } + } + ]; +``` + +### Column shorthand + +A column definition may also be specified simply as a string, in which case the +value of the string is interpreted as the label of the column. Thus, the +simplest column structures can be more succinctly written: + +```js +var grid = new Grid({ + columns: { + first: "First Name", + last: "Last Name", + // ... + }, + // ... +}, "grid"); +``` + +### Subrows + +The Grid component also supports structures with multiple "subrows"; that is, it +supports the idea of rendering multiple rows for each data item. Specification +of multiple subrows is very much like specifying columns, except that one uses +the `subRows` property instead of `columns`, and it receives an array of columns +objects/arrays. Both the `columns` and `subRows` properties can be later reset +by using the central `set` method. + +```js +require([ + "dgrid/Grid" +],function(Grid){ + var grid = new Grid({ + subRows: [ + [ + { field: "id", label: "ID" }, + { field: "name", label: "Name" } + ], + [ + { field: "description", label: "Description", colSpan: 2 } + ] + ], + store: memoryStore + }, "grid"); +}); +``` + +## Column Definition Properties + +In any of the above formats, each individual column definition object may have +the following properties (all are optional): + +Property | Description +-------- | ------------- +`field` | The property from the object in the list to display in the body of the grid (unless otherwise overridden via the `get` function, explained below). In cases where `columns` is passed an object, the key of each property represents the field name, and thus this property is normally omitted. +`label` | The label to show in the header of the grid. Defaults to the value of `field`. +`className` | CSS class(es) to assign to the cells in the column. The value of this property may be a string to apply equally to all cells in the column, or a function which is passed the item for each row (or `undefined` for the header row) and should return a string. In either case, multiple classes may be specified space-delimited. In addition, a class in the format `field-` is added if the column has a `field` defined. +`id` | The id of the column; normally this is determined automatically from the keys or indices in the `columns` object or array. +`sortable` | Indicates whether or not the grid should allow sorting by values in this field, by clicking on the column's header cell. Defaults to `true`. Note that it is always possible to programmatically sort a Grid by a given field by calling `set("sort", property, descending)` regardless of`sortable` status or even visible presence in the Grid altogether. +`get(item)` | An optional function that, given a data item, will return the value to render in the cell. +`set(item)` | An optional function that, given a modified data item, will return the value to set for the respective field on that item upon a call to `save()`. If no value is returned, the value as set in the passed item will be used. (Modifying the passed item directly is thus also an option.) +`formatter(value)` | An optional function that, given the value to be displayed, will return a string of HTML for rendering. If `formatterScope` is used, this can be a string instead of a function, in which case a function will be looked up on the `formatterScope` object using the given string. +`renderCell(object, value, node, options)` | An optional function that will be called to render the value into the target cell. `object` refers to the record from the grid’s store for the row, and `value` refers to the specific value for the current cell (which may have been modified by the column definition’s `get` function). `node` refers to the table cell that will be placed in the grid if nothing is returned by `renderCell`; if `renderCell` returns a node, that returned node will be placed in the grid instead. (Note: if `formatter` is specified, `renderCell` is ignored.) +`renderHeaderCell(node)` | An optional function that will be called to render the column's header cell. Like `renderCell`, this may either operate on the node directly, or return a node to be placed within it. \ No newline at end of file diff --git a/doc/components/core-components/GridFromHtml.md b/doc/components/core-components/GridFromHtml.md new file mode 100644 index 000000000..86b0794f1 --- /dev/null +++ b/doc/components/core-components/GridFromHtml.md @@ -0,0 +1,79 @@ +# GridFromHtml + +Some developers prefer specifying column layouts in an actual table structure +because it is more convenient or semantically clearer. dgrid supports this via +the GridFromHtml module. + +```html + + + + + + + +
IDName
+ + +``` + +GridFromHtml can also be used for store-based grids by additionally +mixing in [OnDemandList](OnDemandList-and-OnDemandGrid.md) or [Pagination](../extensions/Pagination.md). + +## Usage + +When using this module, a `table` element should be specified as the source node +for the grid instance; it then scans for `th` nodes within rows (typically placed +inside the `thead`) to determine columns. Column properties are specified within +the `th`, primarily via the `data-dgrid-column` attribute, which should contain a +JavaScript object. Properties which coincide with standard HTML attributes can +also be specified as such, e.g. `class`, `rowspan`, and `colspan`. The innerHTML +of the `th` is interpreted as the column's `label` by default. + +Note that *unlike* `data-dojo-props`, `data-dgrid-column` requires that you +include the surrounding curly braces around the object - this allows +alternatively specifying a column plugin instead of just a straight-up object. +(Note, however, that referencing column plugins requires that they be exposed in +the global scope, perhaps under a namespace.) Examples of creating grids from +HTML can be found in the `GridFromHtml.html` and `complex_columns.html` test +pages. + +It is also possible to specify columnsets (for the `ColumnSet` mixin) via +HTML tables by using the `GridWithColumnSetsFromHtml` module. ColumnSets are +expressed in HTML via `colgroup` tags. See the `complex_columns.html` test page +for an example of this as well. + +## Dojo 1.7 Note: Using GridFromHtml with the Dojo Parser + +Using the parser in Dojo 1.7 with modules designed purely in the AMD format can +be a bit unwieldy, since the parser still expects `data-dojo-type` values to +reference variables accessible from the global context. While existing Dojo 1.x +components currently continue to expose globals, dgrid does not do so by +default, since it is written purely in AMD format. Thus, when intending to parse +over dgrid components, it is necessary to expose the components via a global +namespace first. For example: + +```js +require(["dgrid/GridFromHtml", "dojo/parser", ..., "dojo/domReady!"], +function(GridFromHtml, parser, ...) { + window.dgrid = { GridFromHtml: GridFromHtml }; + parser.parse(); +}); +``` + +This can be seen in practice in some of the test pages, such as +`GridFromHtml.html` and `dijit_layout.html`. + +This applies to Dojo 1.7 only; in Dojo 1.8 this becomes easier thanks to +`data-dojo-type` supporting module IDs (see +[Dojo ticket #13778](http://bugs.dojotoolkit.org/ticket/13778)). Additionally, +`data-dojo-mixins` can be used to declaratively compose dgrid instances +including mixins. \ No newline at end of file diff --git a/doc/components/core-components/List.md b/doc/components/core-components/List.md new file mode 100644 index 000000000..98ca28c42 --- /dev/null +++ b/doc/components/core-components/List.md @@ -0,0 +1,77 @@ +# List + +This provides the basic facilities for taking an array of objects and rendering +them as rows of HTML in a scrollable area. This will automatically include touch +scrolling capabilities (via the `TouchScroll` module) on mobile devices. + +The List module can be used to render an array of data as follows: + +```js +require(["dgrid/List"], function(List){ + // attach to a DOM element indicated by its ID + var list = new List({}, "list"); + // render some data + list.renderArray(arrayOfData); +}); +``` + +## APIs + +The base List class (inherited by all other classes) exposes specific methods +and maintains the various key DOM / property references. + +### Constructor + +As seen in the example above, the `List` constructor (as well any derivative +dgrid constructors, e.g. `Grid`, `OnDemandGrid`, etc.) can accept 2 properties: +an object containing initial properties to set on the instance, and a DOM node +or string referencing a DOM node's ID (the `srcNodeRef`). +If a `srcNodeRef` is provided, the given DOM node will become the instance's +`domNode`; otherwise the instance will need to be placed into the document manually. + +Each dgrid instance's `domNode` is given a unique ID, based on one of the +following criteria: + +* If a `srcNodeRef` was provided and has an ID, this ID will be used +* If an `id` was specified in the arguments object, that will be used +* Otherwise, an ID will be automatically generated, in the form `dgrid_` + +It is important to note that DOM node IDs are not allowed to contain spaces; +therefore, one should never include spaces in the `id` property. + +### Property Summary + +Property | Description +-------- | ----------- +`domNode` | The top-level DOM node of the component (much like the `domNode` property of Dijit widgets). +`headerNode` | The DOM node representing the header region; mainly applicable to grid components. +`bodyNode` | The DOM node representing the body region (the area which will show rows for each item). +`contentNode` | The DOM node immediately under the `bodyNode`, which may potentially be scrolled to accommodate more content than the component's height will allow to fit. +`footerNode` | A DOM node appearing below the `bodyNode`; initially empty and not displayed by default. +`addUiClasses` | Boolean indicating whether to add `ui-` classes (corresponding to jQuery UI) to nodes within the grid. The default is `true`. +`className` | A class (or classes, space- or period-delimited) to be set on the instance's `domNode` in addition to the defaults; if an existing DOM node is being passed to the constructor, extra classes may alternatively be specified via its `class` attribute or `className` property +`cleanAddedRules` | Whether to clean up CSS rules added via the `addCssRule` method when the instance is destroyed; defaults to `true`. +`cleanEmptyObservers` | Whether to clean up observers for result sets that are or become empty; defaults to `true` but is set to `false` by the `tree` plugin due to special considerations. +`showHeader` | Whether to display the header area; normally `false` for lists and `true` for grids. Can be reset later via `set("showHeader", ...)`. +`showFooter` | Whether to display the footer area; `false` by default, but enabled and used by some extensions (e.g. Pagination). Can be reset later via `set("showFooter", ...)`. +`sort` | How to initially sort the grid; may be either a string referencing a field name, or an array of objects with `attribute` and `descending` properties. See also `set("sort", ...)` below. +`useTouchScroll` | Whether to use the `TouchScroll` module if touch support is available on the device, or defer to native capabilities if they exist; `true` by default for backwards compatibility. Setting this to `false` is particularly useful for devices which support both touch and mouse input. + +### Method Summary + +Method | Description +------ | ------------- +`get(property)` | Returns the value of a given property. Supports custom getter implementations via the pattern `_getProperty` (which would map to `get("property")`). +`set(property, value)` | Sets the value of a given property. Supports custom setter implementations via the pattern `_setProperty` (which would map to `set("property", ...)`). +`set(object)` | Alternate means of invoking `set`, equivalent to calling `set(property, value)` for each key/value pair in the provided object. +`row(target)` | This will look up the requested row and return a Row object. The single parameter may be a DOM event, DOM node, or in the case of store-backed components, a data object or its ID. The returned Row object has the following properties: `id` - the data object's id, `data`- the data object represented by the row, `element`- the row's DOM element +`on(event, listener)` | Basic event listener functionality; simply delegates to the top-level DOM element of the List, using standard `dojo/on` behavior. +`up(row[, steps])` | Given a row object (or something that resolves to one via the `row` method), returns a row object representing the row located `steps` rows above (where `steps` defaults to `1`). +`down(row[, steps])` | Same as `up()`, but operating downward. +`renderArray(array, beforeNode, options)` | This can be called to render an array directly into the list. The `beforeNode` parameter can be used to render at a specific point in the list. Note that when using store-backed components, this is called automatically. +`renderRow(item, options)` | This method can be overridden to provide custom rendering logic for rows. (The [Grid](Grid.md) module actually overrides this method.) `item` refers to the record from the array or store for the row. +`removeRow(rowElement, justCleanup)` | This method can be extended/aspected to perform cleanup logic when an individual row is removed. +`set("sort", property, descending)` | This can be called to sort the List by a given property; if `true` is passed for `descending`, the sort will be in descending order. Multiple sort criteria can be specified in the format expected by stores' `queryOptions` (an array of objects with `attribute` and `descending` properties); this is also the format `get("sort")` will return in. The [Grid](Grid.md) and [OnDemandList)(OnDemandList-and-OnDemandGrid.md) modules further extend sort functionality. +`scrollTo(options)` | scrolls to a given point in the grid. Accepts `x` and `y` properties; if one is not given, position along that axis is not modified. +`getScrollPosition()` | returns the current position that the grid is scrolled to, in the form of an object containing `x` and `y` properties. +`addCssRule(selector, css)` | Programmatically adds styles for a given CSS selector by injecting the given rule string into a stylesheet in the document. Returns a handle with a`remove` function, which can be called to later remove the added style rule. Styles added via this method will be removed when the instance is destroyed if `cleanAddedRules` is set to `true`. \ No newline at end of file diff --git a/doc/components/core-components/OnDemandList-and-OnDemandGrid.md b/doc/components/core-components/OnDemandList-and-OnDemandGrid.md new file mode 100644 index 000000000..f637d2c94 --- /dev/null +++ b/doc/components/core-components/OnDemandList-and-OnDemandGrid.md @@ -0,0 +1,126 @@ +# OnDemandList and OnDemandGrid + +## OnDemandList + +OnDemandList extends List to provide on-demand lazy loading of data as the user +scrolls through the list. This provides a seamless, intuitive interface for +viewing large sets of data in scalable manner. + +```js +require(["dgrid/OnDemandList", "put-selector/put"], function(OnDemandList, put){ + var list = new OnDemandList({ + store: myStore, // a Dojo object store + renderRow: function(object, options){ + // Override renderRow to accommodate store items + return put("div", object.myField); + } + }); +}); +``` + +## OnDemandGrid + +This module is simply the composition of [Grid](Grid.md) and OnDemandList. For example: + +```js +define(["dgrid/OnDemandGrid"], function(OnDemandGrid){ + grid = new OnDemandGrid({ + store: myStore, // a Dojo object store + columns: [ + {label: "Column 1", field: "col1", sortable: false}, + {label: "Column 2", field: "col2"}, + // ... + ] + }, "grid"); + // ... +}); +``` + +## Usage + +OnDemandList inherits the \_StoreMixin module, which implements a basis for +interacting with a [Dojo object +store](http://dojotoolkit.org/reference-guide/dojo/store.html) for querying of +data. At minimum, this implementation expects a store which supports the `get`, +`getIdentity`, and `query` methods, and whose items include unique identifiers. + +OnDemandList requires that a store be specified via the `store` property, and +will call the `query` method on the store to retrieve the data to be rendered. +OnDemandList will call `query` with `start` and `count` options so as to only +retrieve the necessary objects needed to render the visible rows. As the list or +grid is scrolled, more `query` calls will be made to retrieve additional rows, +and previous rows will be pruned from the DOM as they are scrolled well out of +view. + +When working with a writable store, for best results, the store should return +query results with an `observe` method, which enables the list to keep its +display up to date with any changes that occur in the store after the items are +rendered. The +[`dojo/store/Observable`](http://dojotoolkit.org/reference-guide/dojo/store/Observable.html) +module can prove useful for adding this functionality. + +## APIs + +### Property Summary + +Property | Description +-------- | ----------- +`minRowsPerPage` | The minimum number of items that will be requested at one time while scrolling; default is `25`. +`maxRowsPerPage` | The maximum number of items that will be requested at one time while scrolling; default is `250`. +`maxEmptySpace` | The maximum size (in pixels) of unrendered space below or above the rendered portion of the component; default is `Infinity`, which indicates that the size of unrendered space should approximate the total space which would be occupied by all items in the result set. +`bufferRows` | The number of rows to keep rendered beyond each end of the currently visible area of the component; default is `10`. +`farOffRemoval` | The minimum distance (in pixels) which must exist between the currently visible area and previously-rendered rows before they are removed from the DOM; default is `2000`, but this can be adjusted based on a known maximum height in cases where keeping fewer nodes in the DOM is preferable. +`queryRowsOverlap` | Specifies the number of items to "overlap" between queries, which helps ensure consistency of observed updates to items at page boundaries. The default is `1`. +`pagingMethod` | Specifies the method from the `dgrid/util/misc` module to use for throttling the scroll handler; defaults to `"debounce"` to wait until scrolling stops, but can also be set to `"throttleDelayed"` to load even as scrolling continues. +`pagingDelay` | Specifies the number of milliseconds to debounce or throttle scroll handler calls (and thus also potential store requests); default is `15`. +`keepScrollPosition` | Whether to attempt preserving scroll position by default on all `refresh` operations (including sort); defaults to `false`. This can also be set per-refresh by passing an object with a `keepScrollPosition` property to the `refresh` function. + +*Inherited from _StoreMixin* + +Property | Description +-------- | ----------- +`noDataMessage` | An optional message to be displayed when no results are returned by a query. +`loadingMessage` | An optional message to be displayed in the loading node which appears when a new page of results is requested. +`getBeforePut` | if `true` (the default), any `save` operations will re-fetch the item from the store via a `get` call, before applying changes represented by dirty data. +`query` | An object to be passed when issuing store queries, which may contain filter criteria. +`queryOptions` | An object to be passed along with `query` when issuing store queries. Note that the standard `start`, `count`, and `sort` properties are already managed by OnDemandList itself. +`store` | An instance of a `dojo/store` implementation, from which to fetch data. + +### Method Summary + +Method | Description +------ | ----------- +`refresh(options)` | Clears the grid and re-queries the store for data. If `keepScrollPosition` is `true` on either the instance or the `options` passed to `refresh`, an attempt will be made to preserve the current scroll position. OnDemandList returns a promise from `refresh`, which resolves when items in view finish rendering. The promise resolves with the QueryResults that were rendered. + +*Inherited from _StoreMixin* + +Method | Description +------ | ----------- +`set("query", query[, queryOptions])` | Specifies a new `query` object (and optionally, also `queryOptions`) to be used by the list when issuing queries to the store. +`set("store", store[, query[, queryOptions]])` | Specifies a new store (and optionally, also `query` and `queryOptions`) for the list to reference. +`set("sort", property, descending)` | \_StoreMixin's version of this defers sorting to the store. +`updateDirty(id, field, value)` | Updates an entry in the component's dirty data hash, to be persisted to the store on the next call to `save()`. +`save()` | Instructs the list to relay any dirty data back to the store. Returns a promise which resolves when all necessary put operations have completed successfully (even if the store operates synchronously). +`revert()` | Clears the dirty data hash without updating the store, and refreshes the component. + +## Events + +### dgrid-refresh-complete + +As of dgrid 0.3.5, OnDemandList emits a `dgrid-refresh-complete` event when +results finish rendering as the result of a `refresh` call (also including the +initial render). The event includes the following properties: + +* `grid`: The Grid (or List) instance responsible for firing the event +* `results`: The QueryResults returned by the store query + +### dgrid-error + +If something should go wrong within OnDemandList's logic, \_StoreMixin is +equipped to emit a `dgrid-error` event, including the following properties: + +* `grid`: The Grid (or List) instance on which the error occurred +* `error`: The error which occurred + +OnDemandList emits a `dgrid-error` event for any error that occurs while +querying the store for data in reaction to scrolling or a `refresh` call. \ No newline at end of file diff --git a/doc/components/extensions/ColumnHider.md b/doc/components/extensions/ColumnHider.md new file mode 100644 index 000000000..e7bf4162e --- /dev/null +++ b/doc/components/extensions/ColumnHider.md @@ -0,0 +1,52 @@ +# ColumnHider + +The ColumnHider extension adds the ability to dynamically hide or show columns +in a grid without the need to fully reset its layout. User interaction is +enabled via a menu accessible from the top right corner of the grid +(represented by a "+" mark); it will open on click, presenting checkboxes for +each column in the grid. These can be checked or unchecked to show or hide +individual columns, respectively. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/extensions/ColumnHider" +], function(declare, OnDemandGrid, ColumnHider){ + var grid = new (declare([OnDemandGrid, ColumnHider]))({ + columns: { + col1: { + label: "Column 1", + hidden: true + }, + col2: { + label: "Column 2", + unhidable: true + }, + col3: "Column 3" + } + }, "grid"); + // ... +}); +``` + +**Note:** This extension is only fully supported for cases of simple, single-row +column layouts. + +## Additional Column Definition Properties + +The ColumnHider extension supports the following additional column definition properties. + +Property | Description +-------- | ----------- +`hidden` | If `true`, the column in question will be initially hidden, but can be shown by opening the menu and checking its box. +`unhidable` | If `true`, the column in question will not be listed in the menu, denying access to toggle its appearance. This can be particularly useful for a selector column which should always be shown, for example. + +## Events + +The ColumnHider extension will emit a`dgrid-columnstatechange` event when a +column is hidden or shown via the popup. It includes the following properties: + +* `grid`: The Grid instance in which this event occurred +* `column`: The column definition object representing the column that was + hidden or shown +* `hidden`: Boolean representing the new state of the column: `true` if + hidden, `false` if shown \ No newline at end of file diff --git a/doc/components/extensions/ColumnReorder.md b/doc/components/extensions/ColumnReorder.md new file mode 100644 index 000000000..029308d21 --- /dev/null +++ b/doc/components/extensions/ColumnReorder.md @@ -0,0 +1,47 @@ +# ColumnReorder + +The ColumnReorder extension adds the ability to reorder the columns of a grid +via drag'n'drop operations on column headers. In the case of more complex grid +structures, note that reordering is only supported between columns within the +same subrow or columnset. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/extensions/ColumnReorder" +], function(declare, OnDemandGrid, ColumnReorder, declare){ + var grid = new (declare([OnDemandGrid, ColumnReorder]))({ + columns: { + col1: { + label: "Column1", + reorderable: false + }, + col2: "Column 2", + col3: "Column 3" + } + }, "grid"); + // ... +}); +``` + +## Additional Column Definition Properties + +The ColumnReorder extension supports the following additional column definition properties. + +Property | Description +-------- | ----------- +`reorderable` | If explicitly set to `false`, the given column's header node will not be treated as a viable DnD item, preventing it from being reordered. **Note:** this is generally only useful for columns at either end of a structure, since if it is surrounded by neighbors which are reorderable, it will not prevent reordering of the surrounding columns. + +## Events + +The ColumnReorder extension emits a `dgrid-columnreorder` event whenever the +user triggers a column reorder operation. This event includes the following +properties: + +* `grid`: The grid instance in which the reorder operation took place +* `subRow`: The specific subrow in which the reorder operation took place, + reflecting the new order +* `column`: The column definition object representing the column which was + reordered + +This event bubbles and is cancelable; if canceled, the column structure prior +to the reorder operation will be preserved. \ No newline at end of file diff --git a/doc/components/extensions/ColumnResizer.md b/doc/components/extensions/ColumnResizer.md new file mode 100644 index 000000000..37b835644 --- /dev/null +++ b/doc/components/extensions/ColumnResizer.md @@ -0,0 +1,62 @@ +# ColumnResizer + +The ColumnResizer extension can be used to add column resizing functionality (accessible via mouse or touch drag). + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/extensions/ColumnResizer" +], function(declare, OnDemandGrid, ColumnResizer){ + var grid = new (declare([OnDemandGrid, ColumnResizer]))({ + columns: { + col1: { + label: "Column1", + resizable: false + }, + col2: "Column 2", + col3: "Column 3" + } + }, "grid"); + // ... +}); +``` + +## APIs + +The ColumnResizer extension supports the following additional instance properties. + +### Property Summary + +Property | Description +-------- | ----------- +`adjustLastColumn` | If `true` (the default), adjusts the last column's width to "auto" at times where the browser would otherwise stretch all columns to span the grid. +`minWidth` | Minimum width of each column in the grid, in px; default is `40`. + +### Method Summary + +Method | Description +------ | ----------- +`resizeColumnWidth(columnId, width)` | Resizes the width of the column with id `columnId` to be `width` pixels wide. + +## Additional Column Definition Properties + +The ColumnResizer extension supports the following additional column definition properties. + +Property | Description +-------- | ----------- +`resizable` | If `false`, prevents the given column from being resized; default is `true`. +`width` | Optional number specifying an initial width (in pixels) for the column. This is offered as a convenience for dynamically restoring persisted column widths; in most cases, column styling should still be done separately via CSS. + +## Events + +The ColumnResizer extension will emit a`dgrid-columnresize` event when a column +is resized, which includes the following properties: + +* `grid`: The Grid instance in which this event occurred +* `columnId`: The ID of the resized column, which can be used to retrieve the + column from `grid.columns` +* `width`: The new width of the column +* `parentType`: If the event was triggered by user interaction, this property + indicates what type of event originally triggered the event + +The `dgrid-columnresize` event is cancelable; if canceled, the prior size of +the column will be preserved. \ No newline at end of file diff --git a/doc/components/extensions/CompoundColumns.md b/doc/components/extensions/CompoundColumns.md new file mode 100644 index 000000000..1ce8446e0 --- /dev/null +++ b/doc/components/extensions/CompoundColumns.md @@ -0,0 +1,54 @@ +# CompoundColumns + +The CompoundColumns extension adds the ability to define a column structure +which includes additional spanning header cells above the actual columns in the +grid. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/extensions/CompoundColumns" +], function(declare, OnDemandGrid, CompoundColumns){ + var compoundGrid = new (declare([OnDemandGrid, CompoundColumns]))({ + columns:[ + { label: "Full Name", children: [ + { label: "Given", children: [ + { field: "firstname", label: "First" }, + { field: "middlename", label: "Middle", sortable: false } + ] }, + { field: "lastname", label: "Last" } + ] }, + { field: "age", label: "Age" } + ] + }, "compoundGrid"); + // ... +}); +``` + +For an example of the CompoundColumns extension in use, see +`dgrid/test/extensions/CompoundColumns.html`. + +## Usage + +CompoundColumns works by defining any compound header cells first, then defining +the more granular cells via a `children` property of each column definition +object. This can be extended to nest any number of levels. + +In the above example, the `columns` array would define a structure which +presents 4 columns (first name, middle name, last name, and age), with an +additional label cell spanning above first and middle name, and yet another +label cell spanning above all 3 name fields combined. + +Additionally, the headers on the innermost children can be suppressed by +specifying `showChildHeaders: false` on their immediate parent. + +### Mixin Order + +`CompoundColumns` contains special considerations for interoperating with a few +other dgrid mixins and extensions. As such, the following should be kept in +mind when using these components together: + +* `CompoundColumns` should be mixed in **before** `ColumnSet`, + because it needs to normalize its column structure before `ColumnSet` performs its logic. +* `CompoundColumns` should be mixed in **after** `ColumnResizer` and `ColumnHider`, + because it extends methods defined by these other extensions, + to add considerations specific to its own behavior. \ No newline at end of file diff --git a/doc/components/extensions/DijitRegistry.md b/doc/components/extensions/DijitRegistry.md new file mode 100644 index 000000000..4adecb0cf --- /dev/null +++ b/doc/components/extensions/DijitRegistry.md @@ -0,0 +1,22 @@ +# DijitRegistry + +The DijitRegistry extension adds a couple of considerations for working with +Dijit, particularly useful when a dgrid instance is to live inside of a Dijit +layout widget. Its primary purpose is to add dgrid instances to the Dijit +registry, as well as provide a couple of methods and properties which Dijit +widgets will generally expect to exist. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/extensions/DijitRegistry" +], function(declare, OnDemandGrid, DijitRegistry){ + var grid = new (declare([OnDemandGrid, DijitRegistry]))({ + store: myStore, + columns: myColumns + }, "grid"); +}); +``` + +## Usage + +See [Working with Widgets](../../usage/Working-with-Widgets.md) for more information. \ No newline at end of file diff --git a/doc/components/extensions/DnD.md b/doc/components/extensions/DnD.md new file mode 100644 index 000000000..02ee8b4bb --- /dev/null +++ b/doc/components/extensions/DnD.md @@ -0,0 +1,74 @@ +# DnD + +The DnD extension can be used to add row drag'n'drop functionality. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/Selection", "dgrid/extensions/DnD", "dojo/dnd/Source" +], function(declare, OnDemandGrid, Selection, DnD, DnDSource){ + var grid = new (declare([OnDemandGrid, Selection, DnD, Keyboard]))({ + store: myStore, + columns: { + name: "Name" + // ... + } + }, "grid"); + + // Set up another target + var target = new DnDSource("target", { + accept: ["dgrid-row"], + isSource: false + // Optionally, override onDrop(source, nodes) with custom behavior + }); +}); +``` + +## Requirements + +The DnD extension assumes usage of a store-backed component, most commonly an +[OnDemandGrid](../core-components/OnDemandList-and-OnDemandGrid.md#ondemandgrid) instance. The store should be +order-aware, supporting the `options.before` parameter on `add()` and `put()` +calls to properly respond to DnD operations. Additionally, if the store supports +a `copy` method, it will be called for DnD copy operations within the same +list/grid (since a `put` would normally relocate the item). + +Note that the DnD extension inherits the [Selection](../mixins/Selection.md) mixin, which allows it to +behave more resiliently when dragging items. + +## API + +The DnD extension supports the following additional instance properties. + +### Property Summary + +Property | Description +-------- | ----------- +`dndSourceType` | String specifying the type of DnD items to host and accept. Defaults to `dgrid-row`. +`dndParams` | Object to be passed as the second argument to the DnD Source constructor. Note that the `accept` DnD parameter is set to match `dndSourceType` by default, but this may be overridden. +`dndConstructor` | Constructor from which to instantiate the DnD Source for the grid. This defaults to the `GridSource` constructor defined and exposed by the DnD module itself. It is recommended to only override this value with a constructor which extends `GridSource`. + +## Touch Support + +As of Dojo 1.8, the `dojo/dnd` package supports interaction via touch events. +However, since touch events are also used to control scrolling of dgrid +components on touch devices, a conflict ensues and TouchScroll wins by default. +There are generally two ways to resolve this conflict: + +* Increase `touchesToScroll` to 2 on the instance, to require that two fingers + be used to scroll it. +* Set up DnD handles somewhere in the grid, and instruct the DnD source to + only drag by handles by passing `withHandles: true` in `dndParams`. + +Examples of both of these solutions can be found in +`test/extensions/DnD_touch.html`. + +## Styling DnD Avatars + +Styling can be accomplished the same as for any element that uses `dojo/dnd`. +See the [CSS classes section](http://dojotoolkit.org/reference-guide/dojo/dnd.html#id10) +of the `dojo/dnd` Reference Guide page for details. + +The following are generally the primary classes of interest: + +* `dojoDndAvatar`: the top-level node of the avatar; useful for setting dimensions +* `dojoDndAvatarItem`: the content node of the avatar; useful for setting font properties \ No newline at end of file diff --git a/doc/components/extensions/Pagination.md b/doc/components/extensions/Pagination.md new file mode 100644 index 000000000..612436177 --- /dev/null +++ b/doc/components/extensions/Pagination.md @@ -0,0 +1,74 @@ +# Pagination + +In contrast to the [OnDemandList and OnDemandGrid](../core-components/OnDemandList-and-OnDemandGrid.md) modules, +the Pagination extension implements classic discrete paging controls. It displays a certain +number of results at a given time, and provides a footer area with controls to +switch between pages. + +```js +require([ + "dojo/_base/declare", "dgrid/Grid", "dgrid/extensions/Pagination" +], function(declare, Grid, Pagination){ + var grid = new (declare([Grid, Pagination]))({ + store: myStore, + columns: myColumns, + pagingLinks: 1, + pagingTextBox: true, + firstLastArrows: true, + pageSizeOptions: [10, 15, 25] + }, "grid"); + // ... +}); +``` + +**Note:** the Pagination extension should be mixed into List or Grid, **not** +one of the OnDemand constructors, since those contain their own virtual +scrolling logic. Internally, Pagination inherits from the same \_StoreMixin +module inherited by the OnDemand prototypes for common integration with +`dojo/store`. + +## API + +The Pagination extension supports the following additional instance properties and methods. + +In addition, the Pagination extension also inherits properties and methods +supported by `dgrid/_StoreMixin`; for details, see the tables for properties and +methods inherited from \_StoreMixin in the documentation for +[OnDemandList and OnDemandGrid](../core-components/OnDemandList-and-OnDemandGrid.md). + +### Property Summary + +Property | Description +-------- | ----------- +`rowsPerPage` | Number of items to show on a given page. Default: `10` +`previousNextArrows` | Whether to show arrows which go to the previous/next pages when clicked. Default: `true` +`firstLastArrows` | Whether to show arrows which jump to the first/last pages when clicked. Default: `false` +`pagingLinks` | If a positive number is specified, renders a sequence of page numbers around the current page, and for the first and last pages. The number specified indicates how many "neighbors" of the current page are rendered in *each* direction. If `0` is specified, no page number sequence is rendered. Default: `2` +`pagingTextBox` | Whether to show a textbox in place of the current page indicator, to allow immediately jumping to a specific page. Default: `false` +`pageSizeOptions` | An optional array specifying choices to present for the `rowsPerPage` property in a drop-down. If unspecified or empty, no drop-down is displayed (the default behavior). +`showLoadingMessage` | Boolean specifying whether to blank the content area and show the loading message while loading a new page; if `false`, the previous content will remain present until new content finishes loading. Default is `true`. + +### Method Summary + +Method | Description +------ | ----------- +`gotoPage(page)` | Loads the indicated page; returns a promise yielding an object containing the `rows` rendered as well as the `results` they represent. **Note:** Page numbers start at 1. +`refresh()` | Clears the grid and re-queries the store for the first page of data. The Pagination extension returns a promise from `refresh`, which resolves when items in the first page finish rendering. The promise resolves with the QueryResults that were rendered. + +## Events + +Like [OnDemandList and OnDemandGrid](../core-components/OnDemandList-and-OnDemandGrid.md), the Pagination extension will emit the following events: + +* `dgrid-refresh-complete` when results finish rendering as the result of a + `refresh` call (also including the initial render) +* `dgrid-error` when an error occurs while attempting to query the store in + response to user interaction + +## Internationalization + +The Pagination extension retrieves the strings for various parts of its UI from +resources in `dgrid/extensions/nls`, via the `dojo/i18n!` plugin. These strings +are stored in an `i18nPagination` property on the grid instance, and can be +overridden if desired. The optimal point in the lifecycle to override any i18n +strings would be in `postMixInProperties`, or in `buildRendering` *before* +calling `this.inherited(arguments)`. \ No newline at end of file diff --git a/doc/components/mixins/CellSelection.md b/doc/components/mixins/CellSelection.md new file mode 100644 index 000000000..b881e038a --- /dev/null +++ b/doc/components/mixins/CellSelection.md @@ -0,0 +1,27 @@ +# CellSelection + +The CellSelection mixin extends upon the functionality of the [Selection](Selection.md) mixin +to provide selection at the cell level instead. + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/CellSelection" +], function(declare, OnDemandGrid, CellSelection){ + var grid = new (declare([OnDemandGrid, CellSelection]))({ + selectionMode: "single", + // ... + }); +}); +``` + +## Differences from Selection mixin + +* The `selection` object stores a nested hash, where the outer hash is + keyed by item ID and the inner hashes are keyed by column ID. +* The `dgrid-select` and `dgrid-deselect` events still fire, but include a + `cells` property containing an array of cell objects, rather than a `rows` + property. +* Whereas Selection's `select`, `deselect`, and `isSelected` methods look up the + passed argument via List's `row` method, CellSelection looks it up via Grid's + `cell` method. +* The `allowSelect` method is passed a cell object instead of a row object. \ No newline at end of file diff --git a/doc/components/mixins/ColumnSet.md b/doc/components/mixins/ColumnSet.md new file mode 100644 index 000000000..1fc0461a5 --- /dev/null +++ b/doc/components/mixins/ColumnSet.md @@ -0,0 +1,56 @@ +# ColumnSet + +The ColumnSet module provides functionality which divides a grid's columns into +multiple distinct sets, each of which manage their columns' horizontal scrolling +independently. This makes it possible to keep certain columns in view even while +others are scrolled out of viewing range. + +When mixing in ColumnSet, instead of specifying `columns` or `subRows`, one +specifies `columnSets`, which is essentially an array of `subRows`. For example, +in pseudocode: + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/ColumnSet" +], function(declare, OnDemandGrid, ColumnSet){ + var grid = new (declare([OnDemandGrid, ColumnSet]))({ + columnSets: [ + // left columnset + [ + [ + { /* columnset 1, subrow 1, column 1 */ }, + { /* columnset 1, subrow 1, column 2 */ } + ], + [ + { /* columnset 1, subrow 2, column 1 */ }, + { /* columnset 1, subrow 2, column 2 */ } + ] + ], + // right columnset + [ + [ + { /* columnset 2, subrow 1, column 1 */ }, + { /* columnset 2, subrow 1, column 2 */ } + ], + [ + { /* columnset 2, subrow 2, column 1 */ }, + { /* columnset 2, subrow 2, column 2 */ } + ] + ] + ], + // ... + }, "grid"); +}); +``` + +More concrete examples of ColumnSet usage can be found in the +`complex_column.html` test page. + +## APIs + +The ColumnSet mixin supports the following additional instance methods. + +### Method Summary +Method | Description +------ | ----------- +`styleColumnSet(columnsetId, css)` | Programmatically adds styles to a columnset, by injecting a rule into a stylesheet in the document. Returns a handle with a `remove` function, which can be called to later remove the added style rule. Styles added via this method will be removed when the instance is destroyed if `cleanAddedRules` is set to `true`. \ No newline at end of file diff --git a/doc/components/mixins/Keyboard.md b/doc/components/mixins/Keyboard.md new file mode 100644 index 000000000..43ed2a61e --- /dev/null +++ b/doc/components/mixins/Keyboard.md @@ -0,0 +1,114 @@ +# Keyboard + +This mixin adds keyboard handling functionality. The arrow keys can be used to +navigate the focus across cells and rows, providing accessibility and ease of +use. The page up and page down keys may also be used for faster navigation, +traversing the number of rows specified in the `pageSkip` property of the +instance. When used with grids, this mixin references the `cellNavigation` +property of the grid instance, to determine whether keyboard navigation and +focus should operate at the individual cell level (`true`, the default) or at +the row level (`false`). + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/Keyboard" +], function(declare, OnDemandGrid, Keyboard){ + var grid = new (declare([OnDemandGrid, Keyboard]))({ + pageSkip: 20, + cellNavigation: false + }, "grid"); +}); +``` + +## APIs + +The Keyboard mixin supports the following additional instance properties and methods. + +### Property Summary + +Property | Description +-------- | ----------- +`cellNavigation` | Boolean indicating whether keyboard navigation should occur at the row or cell level. Defaults to `true` (cell-level) for grids, `false` (row-level) for lists. +`pageSkip` | Number indicating how many rows to navigate when page up or page down is pressed. Defaults to `10`. +`keyMap` | Object hash mapping key codes to callbacks to be executed when the respective keys are pressed within the body of the grid. This is typically augmented by calling `addKeyHandler`, but it can also be completely overridden by passing an object hash to the constructor (or otherwise creating one before `Keyboard#postMixInProperties` executes). The default `keyMap` is exposed via `Keyboard.defaultKeyMap`. +`headerKeyMap` | Object hash mapping key codes to callbacks to be executed when the respective keys are pressed within the header of the grid. This is typically augmented by calling `addKeyHandler`, but it can also be completely overridden by passing an object hash to the constructor (or otherwise creating one before `Keyboard#postMixInProperties` executes). The default `headerKeyMap` is exposed via `Keyboard.defaultHeaderKeyMap`. + +### Method Summary + +Method | Description +------ | ----------- +`addKeyHandler(key, callback, isHeader)` | Registers the given callback to be executed when the indicated key code is pressed. By default the handler is registered for keypresses within the body of the grid; if `isHeader` is true, it is registered for header keypresses instead. +`focus([target])` | Focuses the given target cell or row (depending on the value of `cellNavigation`), or on the last focused target if none is provided. +`focusHeader([cell])` | Focuses the header row. If `cellNavigation` is true, will focus on the given cell, or the last header cell that was focused if none is provided. + +**Note:** The `focus` method can be passed any type of argument acceptable to +List's `row` method or Grid's `cell` method (depending on the value of +`cellNavigation`). + +## Events + +The Keyboard mixin also emits two custom events, `dgrid-cellfocusin` and +`dgrid-cellfocusout`, when keyboard navigation occurs. Both events bubble, and +include the following properties: + +* `grid`: The instance in which the event occurred +* `row` or `cell` (depending on value of `cellNavigation` instance property): A row or cell object representing the target of the event +* `parentType`: If the event was triggered by user interaction, this property indicates what type of event originally triggered the event + +## Customizing Mappings + +As of dgrid 0.3.6, the Keyboard mixin provides two ways to customize handling of keys pressed within the body or header of a grid. + +### Adding Mappings + +In many cases it may be desirable to map keys in addition to the defaults. This can easily be accomplished by calling the `addKeyHandler` method with a key code and a callback to register. This method may be called multiple times for the same key code; Keyboard uses `dojo/aspect` to apply new callbacks in sequence if there is an existing callback registered. For example, to hook up an action to the enter key: + +```js +// Using direct code here; could also use keys.ENTER from dojo/keys +grid.addKeyHandler(13, function(event) { + // Do something in response to a keydown event for Enter +}); +``` + +Note that the Keyboard mixin does not automatically call `preventDefault` or `stopPropagation`; it is up to the discretion of the developer as to whether the event should be canceled or allowed to bubble. However, the default key map does include handlers to call `preventDefault` for events on the spacebar, since various dgrid components make use of the spacebar and some browsers would scroll the viewport if this were not canceled. + +### Overriding Defaults + +In cases where the provided default key mappings are undesirable, they can be overridden by passing `keyMap` and `headerKeyMap` object hashes to the constructor. + +For reference, the default key mappings are available via `Keyboard.defaultKeyMap` and `Keyboard.defaultHeaderKeyMap`. Note, however, that modifying these directly would affect every grid instance created thereafter using the Keyboard mixin. Instead, if some of the defaults are still desired, use `lang.clone` or `lang.mixin` from `dojo/_base/lang` to mix it in and then overwrite some values. For example: + +```js +function doSomethingOnPageUp(event) { + // Do something in response to a keydown event for Page Up +} +function doSomethingOnPageDown(event) { + // Do something in response to a keydown event for Page Down +} + +var grid = new (declare([Grid, Keyboard]))({ + // Override page up and page down, but keep the rest of the defaults. + // Don't forget the first empty object parameter - this prevents from + // accidentally mixing into defaultKeyMap instead! + keyMap: lang.mixin({}, Keyboard.defaultKeyMap, { + 33: doSomethingOnPageUp, + 34: doSomethingOnPageDown + }), + // other properties here... +}); +``` + +### Default Mappings + +The following table outlines the default key mappings, including the functions that provide their behavior, which can be reused if desired. These functions are executed in instance context, and are passed the keydown event received by the delegated event handler responsible for dispatching to all mapped callbacks. + +Key | Action | Function +--- | ------ | -------- +Up | Move focus up by one row or cell (grid body only). | `Keyboard.moveFocusUp` +Down | Move focus down by one row or cell (grid body only). | `Keyboard.moveFocusDown` +Left | Move focus left by one cell (only when `cellNavigation` is `true`). | `Keyboard.moveFocusLeft` +Right | Move focus right by one cell (only when `cellNavigation` is `true`). | `Keyboard.moveFocusRight` +Page up | Move focus up by the number of rows indicated by the `pageSkip` instance property (grid body only). | `Keyboard.moveFocusPageUp` +Page down | Move focus up by the number of rows indicated by the `pageSkip` instance property (grid body only). | `Keyboard.moveFocusPageDown` +Home | In the header, move focus to the first cell (only when `cellNavigation` is `true`); in the body, move focus to the first row in the list or grid. | `Keyboard.moveFocusHome` +End | In the header, move focus to the last cell (only when `cellNavigation` is `true`); in the body, move focus to the last row in the list or grid. | `Keyboard.moveFocusEnd` \ No newline at end of file diff --git a/doc/components/mixins/Selection.md b/doc/components/mixins/Selection.md new file mode 100644 index 000000000..f4fb1bbbd --- /dev/null +++ b/doc/components/mixins/Selection.md @@ -0,0 +1,108 @@ +# Selection + +The Selection module adds selection capability to a list or grid. The resulting +instance(s) will include a `selection` property representing the selected items. +This mixin will also fire batched `dgrid-select` and `dgrid-deselect` events, +which will possess a `rows` property containing an array of Row objects (with +`id`, `data`, and `element`). For example: + +```js +require([ + "dojo/_base/declare", "dgrid/OnDemandGrid", "dgrid/Selection" +], function(declare, OnDemandGrid, Selection){ + var grid = new (declare([OnDemandGrid, Selection]))({ + selectionMode: "single", + // ... + }, "grid"); + grid.on("dgrid-select", function(event){ + // Get the rows that were just selected + var rows = event.rows; + // ... + + // Iterate through all currently-selected items + for(var id in grid.selection){ + if(grid.selection[id]){ + // ... + } + } + }); + grid.on("dgrid-deselect", function(event){ + // Get the rows that were just deselected + var rows = event.rows; + // ... + }); +}); +``` + +## APIs + +The Selection mixin supports the following additional instance properties and methods. + +### Property Summary + +Property | Description +-------- | ----------- +`selection` | The object containing the IDs of the selected objects. The property names correspond to object IDs, and values are `true` or `false` depending on whether an item is selected. +`selectionMode` | String indicating how selection should behave in response to direct user interaction. See Selection Modes below for supported values. +`allowTextSelection` | Optional boolean indicating whether normal text selection within grid cells should be prevented. By default, text selection is prevented unless `selectionMode` is set to `none`; setting this property to `true` or `false` will enable or disable text selection regardless of `selectionMode`. +`deselectOnRefresh`| Determines whether calls to `refresh` (including sorts) also clear the current selection; `true` by default. +`allowSelectAll`| Determines whether the "select-all" action should be permitted via a checkbox selector column or the Ctrl/Cmd+A keyboard shortcut; defaults to `false`. + +### Method Summary + +Method | Description +------ | ----------- +`allowSelect(row)`| Returns a boolean indicating whether the given `row` should be selectable; designed to be overridden. +`select(row[, toRow])`| Programmatically selects a row or range of rows. +`deselect(row[, toRow])`| Programmatically deselects a row or range of rows. +`selectAll()`| Programmatically selects all rows in the component. Note that only rows that have actually been loaded will be represented in the `selection` object. +`clearSelection()`| Programmatically deselects all rows in the component. +`isSelected(row)`| Returns `true` if the given row is selected. + +**Note:** The `select`, `deselect`, and `isSelected` methods can be passed any +type of argument acceptable to List's `row` method; see the [List](../core-components/List.md) APIs for +more information. + + +## Events + +As indicated above, the Selection mixin will emit two custom events: + +* `dgrid-select`: Emitted when one or more rows are selected +* `dgrid-deselect`: Emitted when one or more rows are deselected + +Note that this means that when a user interaction results in the selection being +changed from one row to another, both events will be fired (with the +`dgrid-deselected` event firing first). + +Both of these events expose the following properties (note that `rows` is +*always* an array, even if only one row was (de)selected.) : + +* `grid`: The Grid (or List) instance in which the event occurred +* `rows`: Array containing any rows affected by the event +* `parentType`: If the event was triggered by user interaction, this property indicates what type of event originally triggered the event + + +## Selection Modes + +The Selection mixin, as well as the [CellSelection](CellSelection.md) mixin which extends from +it, are capable of running in one of several different selection modes, as +specified by the `selectionMode` property: + +Mode | Description +---- | ----------- +`extended` | The default mode; allows multiple selection via keyboard modifiers (Shift to select ranges, Ctrl/Cmd to select/deselect multiple separate rows). Clicks or keypresses without holding Shift or Ctrl/Cmd will select only the current target. +`multiple` | Like `extended`, except that clicks or keypresses without holding Shift or Ctrl/Cmd will add to the existing selection. +`single` | Allows only one row to be selected at a time. +`toggle` | (New in 0.3.7) Toggles the selected state of the row being acted upon; useful for touch input. +`none` | Does not allow direct selection of rows via keyboard or mouse events, but still allows selection via direct API calls, or via a [selector](../column-plugins/selector.md) column. + +### Implementing a Custom Selection Mode + +As of dgrid 0.3.7, adding support for a custom selection mode is as simple as +adding a single method. For example, to add support for a custom mode named +`mymode`, implement a method named `_mymodeSelectionHandler`. This method +receives 2 arguments: the event object, and the target that is being selected (a +dgrid row or cell element, depending on whether Selection or [CellSelection](CellSelection.md) +is being used). The method should call `this.select` as appropriate based on +the details of the event. \ No newline at end of file diff --git a/doc/components/utilities/misc.md b/doc/components/utilities/misc.md new file mode 100644 index 000000000..4f5ecaa44 --- /dev/null +++ b/doc/components/utilities/misc.md @@ -0,0 +1,47 @@ +# util/misc + +The `util/misc` module provides miscellaneous utility functions. Currently this +consists primarily of functions related to throttling or debouncing function +calls, which can be particularly useful for events which tend to fire rapidly, +such as scroll. As of dgrid 0.3.5, `util/misc` is also home to the `addCssRule` +function, used by various APIs across dgrid components. + +```js +require(["dgrid/util/misc"], function(misc){ + // Produce a new function which will call `myFunction` a maximum of + // once per second + var myThrottledFunction = misc.throttle(myFunction, null, 1000); +}) +``` + +## Usage + +### Throttling and Debouncing + +For those unfamiliar with the distinction between throttling and debouncing, +they both center around the idea of limiting the frequency in which functions +are called. + +* *Throttling* limits a function to being called at most once within a given + period of time; subsequent calls within the same time window are ignored. +* *Debouncing* involves waiting until a certain amount of time has + elapsed before calling the function; the target function will not be + called at all until no calls have been made within that amount of time. + +## APIs + +### Property Summary + +Property | Description +-------- | ----------- +`defaultDelay` | Default delay (in milliseconds) used for `throttle`, `debounce`, and `throttleDelayed`; default value is `15`. + +### Function Summary + +Function | Description +-------- | ----------- +`addCssRule(selector, rule)` | Programmatically adds styles for a given CSS selector by injecting a rule into a stylesheet in the document. Returns a handle with a`remove` function, which can be called to later remove the added style rule. This function is used by instance methods such as `List#addCssRule` and `Grid#styleColumn`; note however that calling the function in `util/misc` directly will not set up automatic removal at any given point. +`escapeCssIdentifier(id)` | Escapes characters in the given string that would be invalid in a CSS identifier (i.e. a tag name, class name, or id in a selector). Used internally by dgrid modules for calls to `addCssRule` and derivatives. +`throttle(func, context, delay)` | Returns a new function throttling the original. `context` defaults to the global object; `delay` defaults to `defaultDelay`. +`debounce(func, context, delay)` | Returns a new function debouncing the original. `context` and `delay` have the same defaults as for `throttle`. +`throttleDelayed(func, context, delay)` | Like `throttle`, but runs the original function *after* each time window elapses, rather than before. This can be useful for things like scroll events, where the last event is really what matters, but you still want to handle intermediate events as well. \ No newline at end of file diff --git a/doc/components/utilities/mouse.md b/doc/components/utilities/mouse.md new file mode 100644 index 000000000..9e38927be --- /dev/null +++ b/doc/components/utilities/mouse.md @@ -0,0 +1,36 @@ +# util/mouse + +The `util/mouse` module defines a number of extension events which are useful in +situations which require the mouse moving into and/or out of rows or cells. +These scenarios warrant extension events due to the often-problematic bubbling +nature of the `mouseover` and `mouseout` DOM events. + +```js +require(["dgrid/util/mouse"], function(mouseUtil){ + // Assume we have a Grid instance in the variable `grid`... + grid.on(mouseUtil.enterRow, function(event){ + var row = grid.row(event); + // Do something with `row` here in reaction to when the mouse enters + }); +}); +``` + +## Events + +The following extension events are provided: + +* `enterRow`: fires when the mouse moves into a row within the body of a list + or grid. +* `leaveRow`: fires when the mouse moves out of a row within the body of a list + or grid. +* `enterCell`: fires when the mouse moves into a cell within the body of a grid. +* `leaveCell`: fires when the mouse moves out of a cell within the body of a + grid. +* `enterHeaderCell`: fires when the mouse moves into a cell within the header of + a grid. +* `leaveHeaderCell`: fires when the mouse moves out of a cell within the header + of a grid. + +These extension events can be used as indicated in the example above, further +described in the respective section of the +[`dojo/on` Reference Guide](http://dojotoolkit.org/reference-guide/dojo/on.html#extension-events). \ No newline at end of file diff --git a/doc/components/utilities/touch.md b/doc/components/utilities/touch.md new file mode 100644 index 000000000..4fe3bb46e --- /dev/null +++ b/doc/components/utilities/touch.md @@ -0,0 +1,27 @@ +# util/touch + +The `util/touch` module defines two simple extension events, `tap` and `dbltap`, +for detecting the respective actions on touch devices. It also defines utility +functions for handling touch events. + +```js +require(["dgrid/util/touch"], function(touchUtil){ + // Assume we have a Grid instance in the variable `grid`... + grid.on(touchUtil.selector(".dgrid-content .dgrid-row", touchUtil.dbltap), function(event){ + var row = grid.row(event); + // Do something with `row` here in reaction to when it is double-tapped + }); +}); +``` + +## APIs + +In addition to the `tap` and `dbltap` extension events, `util/touch` also +defines the following functions. + +### Function Summary + +Function | Description +-------- | ----------- +`countCurrentTouches(event, node)` | Counts the number of currently active touches which fall within the given node; useful in cases where other handlers may call `stopPropagation`, thus affecting other means of counting touches. +`selector(selector, eventType, children)` | A version of the `selector` function from `dojo/on`, with an additional fix to work around issues experienced on iOS Safari. This is used by `dgrid/Selection` and `dgrid/tree` for touch event handling. \ No newline at end of file diff --git a/doc/migrating/API-Comparison.md b/doc/migrating/API-Comparison.md new file mode 100644 index 000000000..ea60363ce --- /dev/null +++ b/doc/migrating/API-Comparison.md @@ -0,0 +1,279 @@ +# API Comparison Between dojox/grid and dgrid + +## dojox/grid properties + +### sortInfo + +The way in which dgrid represents current sort order is significantly different +than `dojox/grid`. dgrid stores the current sort options, as they would be passed +via a store's `queryOptions`; these options are retrievable via the sort getter +(e.g. `grid.get("sort")`). + +Additionally, store-backed components will reflect any currently-applied sort +information in the object returned by `get("queryOptions")`, since `sort` +becomes part of these options when queries are issued. + +### rowSelector and indirect selection + +Indirect selection is available in dgrid via the selector column plugin. This +achieves similar effects to the DataGrid's `_CheckBoxSelector` and +`_RadioButtonSelector` view types, and EnhancedGrid's IndirectSelection plugin. + +dgrid does not feature a direct analog to the `rowSelector` property. + +### selectionMode + +dgrid supports this property via the Selection and CellSelection mixins. +It recognizes the same values supported by `dojox/grid` components +(`none`, `single`, `multiple`, and `extended`, the latter being the default). + +### keepSelection + +This is roughly the inverse equivalent to the `deselectOnRefresh` property +supported by dgrid's Selection (and CellSelection) mixin. Both `dojox/grid` +and dgrid default to *not* maintaining selection between refreshes, sorts, etc. + +### columnReordering + +Setting this `dojox/grid` property to `true` allows reordering of columns +in grids with basic structures via drag'n'drop operations on column header cells. + +This feature is available in dgrid via the ColumnReorder extension. + +### headerMenu and other context menu scenarios + +dgrid does not directly offer context menu functionality via an extension, but +it is easily possible to delegate to the `contextmenu` event of +cells or rows in the grid's body or header, to perform custom logic. Here are +the basic steps one would need to follow: + +* An event handler listening for `contextmenu` events against a particular selector; + for example: + * `.dgrid-header:contextmenu` for a general header context menu + * `.dgrid-row:contextmenu` for a general body context menu + * `.dgrid-header .field-foo:contextmenu` for a context menu for a specific header cell + * `.dgrid-content .field-foo:contextmenu` for a context menu for body cells in + a particular column +* within the event handler: + * A call to `preventDefault` on the event object, to stop the default + browser context menu from displaying. + * A call to `grid.row()` or `grid.cell()` to retrieve information on the + pertinent row or cell. If the menu is intended to apply to selected items, + `grid.selection` can be checked for entries, and then `grid.row()` can be + called with the IDs found. + * If `dijit/Menu` is being used, it unfortunately does not provide any + directly-accessible public API for simply opening the menu around the mouse, + as it is normally expected to be pre-attached to nodes; however, depending + on use-case, attaching it to an entire section of the grid may be inappropriate. + In such a case, the menu can be directly opened within a `contextmenu` + event handler, by calling `_scheduleOpen(this, null, { x: evt.pageX, y: evt.pageY }`. + +### autoHeight + +Automatic height can be achieved using `height: auto` in the CSS for a grid's +main DOM node. There is no direct programmatic support for this. (This means +there is no built-in support for automatically sizing to a certain number of rows.) + +Examples of an auto-height grid can be found in the `autoheight.html` and +`extensions/Pagination.html` test pages. + +### autoWidth + +Not supported. + +### initialWidth + +Not supported. Width (and height) should be dictated via CSS. + +### singleClickEdit + +The effect of the `singleClickEdit` property can be achieved by specifying +`editOn: "click"` in a column definition passed to the editor column plugin +function. (Alternatively, `dojox/grid`'s default double-click behavior can be +achieved by specifying `editOn: "dblclick"` instead.) + +### loadingMessage, noDataMessage, errorMessage + +Store-backed grid instances support `loadingMessage` and `noDataMessage`. +There is currently no direct support for an error message, but when a +store-related error occurs within dgrid's own logic, it will emit a `dgrid-error` +event. When `grid.save()` is called directly, it will return a promise which +will reject if an error occurs. + +### selectable + +This is not exposed as a distinct option in dgrid, but is automatically managed +by the `Selection` plugin. Standard browser selection is disabled when a +`selectionMode` other than `none` is in use. +Otherwise, text selection operates as normal. + +### formatterScope + +dgrid supports this option as of version 0.3.7. + +### updateDelay + +dgrid does not support this, as it is generally not applicable, due to the +difference in how dgrid components update from observed store changes. + +### escapeHTMLInData + +Not supported. By default, dgrid components will escape HTML in data, as it +should generally be devoid of HTML in most cases, and presence of HTML in data +might suggest a cross-site scripting attempt. + +The `formatter` or `renderCell` functions in the column definition may be +overridden to explicitly render data as received, in cases where that is truly +desired. + +## Column Definitions + +Whereas `dojox/grid` always expects cell definitions to be specified via a +`structure` property, dgrid expects one of the following properties to be specified: + +* `columns`: an array or object hash, for simple single-row grid configurations +* `subRows`: an array of arrays, for grid configurations with multiple sub-rows per item +* `columnSets` (only when the ColumnSet mixin is in use): a nested array for + grid configurations containing distinct horizontal regions of one or more rows + (analogous to multiple views in a `dojox/grid` instance) + +The following subsections outline how features of `dojox/grid` cell definitions +are available in dgrid column definitions. + +### field + +Supported by dgrid, including the special `"_item"` value supported by +`dojox/grid` in Dojo >= 1.4. + +Also note that dgrid also supports specifying `columns` as an object hash instead +of an array, in which case the key of each property is interpreted as the `field`. + +### fields + +Not supported by dgrid. If a compound value is desired, define a custom `get` +function in a column definition. + +### width + +Use CSS with `.field-` selectors. (Note that if any value is +specified via the `className` property of the column definition object, it +takes the place of `.field-`.) + +### cellType, widgetClass + +The editor column plugin provides capabilities equivalent to these properties. +It accepts an `editor` property, which can be either a widget constructor or a +string indicating a native HTML input type. + +### options + +Not directly applicable; in `dojox/grid` this applies only to cell definitions +where `cellType` is set to `dojox.grid.cells.Select`. + +The editor column plugin does not currently offer support for standard HTML +select components; however, similar behavior can be achieved using the +`dijit/form/Select` widget as the `editor`, and specifying `options` for the +widget within the `editorArgs` property of the column definition object. + +### editable + +In dgrid, cells are uneditable by default, and are made editable by invoking the +editor column plugin. + +### draggable + +Only applicable to `dojox/grid` instances with `columnReordering` set to `true`, +the `draggable` property determines whether a particular column can be +reordered via drag'n'drop. + +The ColumnReorder dgrid extension provides an equivalent via the `reorderable` +column definition property. It defaults to `true`, but if set explicitly to +`false`, the given column's header node will not be registered as a DnD item. + +### formatter + +dgrid supports formatter functions, but doesn't support returning a widget from +them. + +dgrid also has `renderCell`, which is expected to return a DOM node. This could +ostensibly be used for displaying widgets (and the editor column plugin does +exactly this). + +Note that for cell editing purposes, use of the editor column plugin is highly +encouraged. + +### get + +dgrid supports the `get` function on column definitions; however, note that it +only receives one parameter: the object for the item represented by the current +row being rendered. (dgrid generally has no concept of row index, since +row identities are generally far more meaningful.) + +### hidden + +The `hidden` property on column definitions is only supported by the +ColumnHider extension. Otherwise, columns would ordinarily be suppressed simply +by excluding them from the `columns`, `subRows` or `columnSets` property outright. + +## DataGrid methods + +### getItem(rowIndex), getItemIndex(item) + +These are somewhat inapplicable, since again, dgrid components do not put any +emphasis on index in terms of order of appearance in the component. + +On the other hand, when dealing with events on nodes in a list or grid, it is +possible to retrieve the associated item via the `data` property of the object +returned by the `row` or `cell` functions. These functions can look up based on +a variety of argument types, including a child node of the target row/cell, or +an event object which fired on such a node. + +### setStore + +Store-backed dgrid components support this via +`set("store", store[, query[, queryOptions]])`. + +### setQuery + +Store-backed dgrid components support this via +`set("query", query[, queryOptions])`. + +### setItems + +While it is unclear what exact purpose this serves in `dojox/grid/DataGrid`, and +whether or not it is truly intended to be public, it is probably analogous to +calling `renderArray` directly on a dgrid component. Note, however, that +generally `renderArray` is not expected to be called directly on store-backed +instances. + +### filter + +This is likely closest in behavior to the `refresh` method on dgrid components. + +### sort + +dgrid components have a sort setter (e.g. `grid.set("sort", ...)` which takes +two arguments: + +* the name of the field to sort by +* optionally, a flag as to whether to sort descending (defaults to `false`) + +Like the `sort` method in `dojox/grid` components, this can even be called to +sort columns that ordinarily wouldn't be sortable via the UI. + +### canSort + +`dojox/grid` components refer to this method when a sort request is initiated +via the UI. dgrid does not have such a method; instead, it relies on the +`sortable` property of each column definition (which defaults to `true`). + +### removeSelectedRows + +dgrid has no direct analog to this method, but the same effect can be achieved +on a store-backed, Selection-enabled list or grid instance as follows: + +```js +for(var id in grid.selection){ + grid.store.remove(id); +} +``` \ No newline at end of file diff --git a/doc/migrating/Usage-Comparison.md b/doc/migrating/Usage-Comparison.md new file mode 100644 index 000000000..d3d53dbd9 --- /dev/null +++ b/doc/migrating/Usage-Comparison.md @@ -0,0 +1,389 @@ +# Usage Comparison Between dojox/grid and dgrid + +## Simple programmatic usage + +Given the following programmatic example using `dojox/grid`... + +```js +require(["dojox/grid/DataGrid", "dojo/store/Memory", "dojo/data/ObjectStore", + "dojo/domReady!"], +function(DataGrid, Memory, ObjectStore){ + var memoryStore = new Memory({data: [ + // data here... + ]}); + var objectStore = new ObjectStore({ objectStore: memoryStore }); + + var grid = new DataGrid({ + structure: [ + { field: "id", name: "ID", width: "10%" }, + { field: "name", name: "Name", width: "20%" }, + { field: "description", name: "Description", width: "70%" } + ], + store: objectStore + }, "grid"); + grid.startup(); +}); +``` + +A result similar to the above example could be achieved using dgrid with the +following styles... + +```css +#dgrid .field-id { + width: 10%; +} +#dgrid .field-name { + width: 20%; +} +#dgrid .field-description { + width: 70%; +} +``` + +...and the following JavaScript... + +```js +require(["dgrid/OnDemandGrid", "dgrid/Keyboard", "dgrid/Selection", + "dojo/_base/declare", "dojo/store/Memory", "dojo/domReady!"], +function(OnDemandGrid, Keyboard, Selection, declare, Memory){ + var memoryStore = new Memory({data: [ + // data here... + ]}); + + var grid = new declare([OnDemandGrid, Keyboard, Selection])({ + columns: { + id: { label: "ID" }, + name: { label: "Name" }, + description: { label: "Description" } + }, + store: memoryStore + }, "grid"); + // dgrid will call startup for you if the node appears to be in flow +}); +``` + +There are a few key differences worth pointing out: + +* Whereas `dojox/grid` expects styles to be specified within the column definition + to be eventually applied inline to all cells in the column, dgrid lets CSS do + the talking whenever possible for purposes of layout and appearance. This + allows for better separation between visual and functional concerns. +* `dojox/grid` operates with stores implementing the earlier `dojo/data` APIs; + in order to use it with a store instance implementing the `dojo/store` APIs, + the store must first be wrapped using the `dojo/data/ObjectStore` module. + On the other hand, dgrid communicates with `dojo/store` APIs out of the box. + (Conversely, however, if you *do* need to work with a `dojo/data` store, you + would then have to pass it through the `dojo/store/DataStore` wrapper in order + for dgrid to work with it.) +* Note that in the dgrid version of the example, the Selection and Keyboard + modules are required and mixed into the constructor to be instantiated, in + order to enable those pieces of functionality which are baked-in by default + in `dojox/grid` components. +* Also note that the dgrid example's structure is a bit more concise, taking + advantage of the ability to provide simple column arrangements via an object + hash instead of an array, in which case the object's keys double as the + columns' `field` values (i.e., which store item properties the columns represent). + +## Programmatic usage, with sub-rows + +Assuming the same context as the examples in the previous section, here is a +contrived example demonstrating use of sub-rows in `dojox/grid`... + +```js +var grid = new DataGrid({ + structure: [ + [ + { field: "id", name: "ID", width: "10%" }, + { field: "name", name: "Name", width: "20%" } + ], + [ + { field: "description", name: "Description", width: "70%", colSpan: 2 } + ] + ], + store: objectStore +}, "grid"); +grid.startup(); +``` + +...and the equivalent, using dgrid... +(again assuming the same context as the previous example) + +```js +var grid = new declare([OnDemandGrid, Keyboard, Selection])({ + subRows: [ + [ + { field: "id", label: "ID" }, + { field: "name", label: "Name" } + ], + [ + { field: "description", label: "Description", colSpan: 2 } + ] + ], + store: memoryStore +}, "grid"); +``` + +Notice that `subRows` is now defined instead of `columns`. The `columns` +property of dgrid components is usable *only* for simple cases involving a +single sub-row. + +Also notice that each item in the top-level `subRows` array is itself another +array, containing an object for each column. In this case, `field` must be +specified in each column definition object, since there is no longer an +object hash in order to infer field names from keys. + +## Using views / columnsets + +The `dojox/grid` components implement a concept known as "views", which are +represented as separate horizontal regions within a single grid. This feature +is generally useful for situations where many fields are to be shown, and some +should remain visible while others are able to scroll horizontally. + +This capability is also available in dgrid, via the ColumnSet mixin. + +For instance, continuing in the vein of the examples in the previous two +sections, the following `dojox/grid` structure with multiple views... + +```js +var grid = new DataGrid({ + structure: [ + { // first view + width: "10%", + cells: [ + { field: "id", name: "ID", width: "auto" } + ] + }, + [ // second view + [ + { field: "name", name: "Name", width: "20%" }, + { field: "description", name: "Description", width: "80%" } + ] + ] + ], + store: objectStore +}, "grid"); +grid.startup(); +``` + +...could be represented in dgrid, using the following CSS... + +```css +#dgrid .dgrid-column-set-0 { + width: 10%; +} +#dgrid .field-name { + width: 20%; +} +#dgrid .field-description { + width: 80%; +} +``` + +...and the following JavaScript... +(require call included, to demonstrate additional dependency) + +```js +require(["dgrid/OnDemandGrid", "dgrid/ColumnSet", "dgrid/Keyboard", "dgrid/Selection", + "dojo/_base/declare", "dojo/store/Memory", "dojo/domReady!"], +function(OnDemandGrid, ColumnSet, Keyboard, Selection, declare, Memory){ + // ... create memoryStore here ... + + var grid = new declare([OnDemandGrid, ColumnSet, Keyboard, Selection])({ + columnSets: [ + [ // first columnSet + [ + { field: "id", label: "ID" } + ] + ], + [ // second columnSet + [ + { field: "name", label: "Name" }, + { field: "description", label: "Description" } + ] + ] + ], + store: memoryStore + }, "grid"); +}); +``` + +## Specifying column layout via HTML + +While programmatic creation of grids is highly encouraged, dgrid does allow for +declarative specification of grid layouts via a `table` element, somewhat along +the same lines of `dojox/grid`. + +In the case of dgrid, this ability is not baked in by default, but is instead +exposed primarily by the GridFromHtml module, which adds table-scanning +capabilities atop the OnDemandGrid constructor. + +Note that unlike `dojox/grid`, which is *only* capable of reading declarative +layouts through the use of `dojo/parser`, dgrid is also capable of creating +instances programmatically while referencing a `table` node from which to read +a declarative layout. For the purposes of the examples below, use of parser +will be assumed, in order to allow comparison between `dojox/grid` and dgrid +usage. + +For instance, the following declarative `dojox/grid` layout... + +```html + + + + + + + + +
IDNameDescription
+``` + +...could be achieved declaratively using dgrid as follows... + +```html + + + + + + + + +
IDNameDescription
+``` + +...provided the following script is used... + +```js +require(["dgrid/GridFromHtml", "dgrid/Keyboard", "dgrid/Selection", + "dojo/store/Memory", "dojo/_base/declare", "dojo/parser", "dojo/domReady!"], +function(GridFromHtml, Keyboard, Selection, Memory, declare, parser){ + var memoryStore = window.memoryStore = new Memory({data: [ + // ... data here ... + ]}); + + // Globally expose a Grid constructor including the mixins we want. + window.dgrid = { + CustomGrid: declare([GridFromHtml, Keyboard, Selection]) + }; + + // Parse the markup after exposing the global. + parser.parse(); +}); +``` + +Notice that rather than specifying individual non-standard attributes inside the +`th` elements, declarative layout specification with dgrid centers primarily +around a data-attribute named `data-dgrid-column`. This attribute receives a +string representation of a JavaScript object, which will ultimately become the +basis for the column definition. (It operates much like `data-dojo-props`, +except that the surrounding curly braces must be included.) + +Note that some properties which have standard equivalents, such as `colspan` and +`rowspan`, can be specified directly as HTML attributes in the element instead. +Additionally, the innerHTML of the `th` becomes the column's label. + +The script block above demonstrates the main catch to using dgrid declaratively +with `dojo/parser`: since the modules in the dgrid package are written to be +pure AMD, they do not expose globals, which means whatever constructors are to +be used need to be exposed manually. Furthermore, rather than simply exposing +the GridFromHtml constructor, the above example exposes a custom-declared +constructor which mixes in desired functionality. + +Note that if column plugins are to be employed, these will also need to be +similarly globally exposed. Column plugins may be specified in the column +definitions of declarative grid layouts within the `data-dgrid-column` attribute; +for example: + +```html +Name +``` + +### Column Layout via HTML with views / columnsets + +While both `dojox/grid` and dgrid also enable declarative creation +of grids with multiple views/columnsets, in dgrid's case this is again separated +to its own module, GridWithColumnSetsFromHtml. This separation exists due to +the significant amount of additional code necessary to resolve columnsets from +the representative markup, combined with the relative rarity of cases calling +for the additional functionality. + +As a quick example, here is what a simple declarative grid with two views could +look like with `dojox/grid`... + +```html + + + + + + + + + + +
IDNameDescription
+``` + +...and here is the equivalent, using dgrid... +(this assumes the same styles are in play as the earlier programmatic ColumnSet +example) + +```html + + + + + + + + + + +
IDNameDescription
+``` + +## Events + +`dojox/grid` and dgrid take significantly different approaches to handling +events. `dojox/grid` provides a wide selection of stub methods which +can be connected to in order to react to many common events on rows or cells in +the header or body. The [Working with Grids](http://dojotoolkit.org/documentation/tutorials/1.7/working_grid/) +tutorial gives an idea of what kinds of events are supported by `dojox/grid`. + +On the other hand, dgrid leaves it up to the developer as to which events are +at all worth listening for. This results in generally far less overhead, since +listeners are hooked up only for events of interest; at the same time, it +still allows for the same range of event listeners as `dojox/grid`. + +`dojox/grid` components generally attach any useful information directly to +the event object received by the handler callback. While dgrid does this to a +certain degree for custom events, the most commonly-sought information is +retrievable using the `row` and `cell` methods. + +See [Working with Events](../usage/Working-with-Events.md) for more information. + +As a quick side-by-side comparison, here is an example logging the name property +of an item whose row was clicked, using `dojox/grid`... + +```js +grid.connect(grid, "onRowClick", function(evt){ + var item = grid.getItem(evt.rowIndex); + // don't forget to use store.getValue, since dojox/grid uses a dojo/data store + console.log("Clicked item with name: " + + grid.store.getValue(item, "name")); +}); +``` + +...and using dgrid... + +```js +grid.on(".dgrid-row:click", function(evt){ + var item = grid.row(evt).data; + console.log("Clicked item with name: " + item.name); +}); +``` \ No newline at end of file diff --git a/doc/usage/Limitations.md b/doc/usage/Limitations.md new file mode 100644 index 000000000..7793de6ae --- /dev/null +++ b/doc/usage/Limitations.md @@ -0,0 +1,68 @@ +# Limitations + +## Use with the Legacy Loader API + +The dgrid package was designed primarily with AMD in mind; as such, it has been +tested primarily using the `require` and `define` APIs available in Dojo 1.7. + +When using the legacy `dojo.require` method instead, loading `dgrid/List` will +not work without first loading `dgrid.css`. This is because otherwise the +`xstyle/css` plugin will resolve asynchronously, which is not suported by the +legacy loader. To use `dgrid/List` with `dojo.require`, make sure you have +`` in your `` before +loading `dgrid/List`. + +This also applies for stylesheets loaded by specific mixins (such as `dgrid/ColumnSet`) +or extensions (such as `dgrid/extensions/ColumnResizer`). + +## Reuse of Column Definitions + +Reusing a single column definition object between multiple grids (e.g. +`var cols = {...}, gridA = new Grid({ columns: cols }), gridB = new Grid({ columns: cols })`) +is *not* supported, and will not function properly. Always create a fresh `columns` +object for every grid you instantiate. This includes performing unique invocations +of column plugin functions for each grid. + +If multiple grids in a single page are likely to use the same column structure, +in order to avoid code repetition, you can create a function which returns +the structure, but creates it every time it is called. For example: + +```js +function getColumns(){ + return { + col1: editor({ label: "Column 1" }, "text", "dblclick"), + col2: { label: "Column 2", sortable: false }, + // ... + }; +} + +var grid = new Grid({ + columns: getColumns(), + // ... +}, "grid"); +var secondGrid = new Grid({ + columns: getColumns(), + // ... +}, "secondGrid"); +``` + +## Column Definitions: Coexistence of formatter and renderCell + +The default `renderCell` logic, as well as the logic within the `editor` and +`tree` column plugins, is specifically written in such a way as to honor any +`formatter` that is defined on the column definition. + +If a custom `renderCell` function is specified, however, it will override the +default logic which is responsible for honoring `formatter`, and thus the custom +`renderCell` logic will take precedence. + +## Note on Observable Stores + +dgrid 0.3.13 contains updates to remediate issues with `dojo/store/Observable` +dropping items at the boundaries of paged result sets when they are modified, +and ignoring items added at the end of the grid. + +After those fixes, however, you may encounter another quirk where items are +always moved to the end of the grid when they are updated. This issue happens +specifically if you have not specified a `sort` order, and can be avoided by +specifying one. \ No newline at end of file diff --git a/doc/usage/Styling-dgrid.md b/doc/usage/Styling-dgrid.md new file mode 100644 index 000000000..cef1187d1 --- /dev/null +++ b/doc/usage/Styling-dgrid.md @@ -0,0 +1,112 @@ +# Styling dgrid + +dgrid components are designed to be highly CSS-driven for optimal performance and +organization, so visual styling should be controlled through CSS. + +## Styling Grid Columns + +The Grid module creates classes based on the field names and column IDs of the columns +defined, following the conventions `field-` and `dgrid-column-`. +You can also explicitly define one or more classes via the `className` property, +in which case it is applied in addition to `field-`. + +For example, you could define a grid and style it like so: + +```html + + +``` + +## Styling Grid Rows + +While the column definition provides mechanisms for adding classes to individual +columns, there is no similar construct at the row level. However, it is possible +to add classes to rows by aspecting `renderRow`. + +In Dojo 1.8 and newer, you can achieve this using `aspect.after` - when its +`receiveArguments` argument is `false` (the default), it passes the original +arguments object as a second parameter to the advising function, allowing access +to the original object: + +```js +aspect.after(grid, "renderRow", function(row, args) { + // Add classes to `row` based on `args[0]` here + return row; +}); +``` + +In Dojo 1.7, you need to use `aspect.around`, in order to have access to both the +incoming object and the resulting row node: + +```js +aspect.around(grid, "renderRow", function(original) { + return function(object) { + var row = original.apply(this, arguments); + // Add classes to `row` based on `object` here + return row; + }; +}); +``` + +## Skinning dgrid Components + +dgrid automatically loads the necessary structural CSS to work properly using +xstyle's css module. However, to make the component far more visually attractive, +it is common to also apply one of the the included skins. There are various +CSS files under the `css/skins` directory which can be used to significantly +alter the look and feel of dgrid components. The `skin.html` test page provides +a quick demonstration of all included skins. + +Many of the classes commonly involved in customizing layout and skin can be discovered +by examining the CSS of existing skins, or by inspecting elements in your browser's +developer tools. + +To help get started, the following is a list of classes commonly employed in the +structuring of dgrid components: + +* `dgrid`: Applied to each dgrid list or grid at the top-level element +* `dgrid-header`: Applied to the element which contains the header area +* `dgrid-scroller`: Applied to the element responsible for scrolling the data content +* `dgrid-content`: Applied to the element inside of the scroller area, + which holds all the data contents +* `dgrid-row`: Applied to each row element +* `dgrid-row-even`: Applied to each even row element +* `dgrid-row-odd`: Applied to each odd row element + Applying a different color to alternating rows can help visually distinguish individual items. +* `dgrid-selected`: Applied to selected rows or cells +* `dgrid-cell`: Applied to each cell element +* `dgrid-cell-padding`: Applied to each cell element, or to an + inner element within the cell in older versions of non-quirks mode IE to + properly apply padding to keep the padding within the box measurements + (box-sizing is preferred by the grid). +* `dgrid-focus`: Applied to the element (cell or row) with the focus (for keyboard based navigation) +* `dgrid-expando-icon`: Applied to the expando icon on tree nodes +* `dgrid-header-scroll`: Applied to the node in the top right corner of a Grid, + above the vertical scrollbar + +When `addUiClasses` is set to `true` (the default), the following generic class +names are also available for generic skinning (following the jQuery ThemeRoller convention): + +* `ui-widget-content`: Applied to each dgrid list or grid at the top element +* `ui-widget-header`: Applied to the element that contains the header rendering +* `ui-state-default`: Applied to each row element +* `ui-state-active`: Applied to selected rows or cells +* `ui-state-highlight`: Applied to a row for a short time when the contents are changed (or it is newly created) \ No newline at end of file diff --git a/doc/usage/Working-with-Events.md b/doc/usage/Working-with-Events.md new file mode 100644 index 000000000..bfd52b2bc --- /dev/null +++ b/doc/usage/Working-with-Events.md @@ -0,0 +1,74 @@ +# Working with Events + +## Listening for events with dojo/on and grid.on + +The [`dojo/on`](http://dojotoolkit.org/reference-guide/dojo/on.html) module, +new to Dojo 1.7, provides a concise yet powerful API for registering listeners, +especially for DOM events. Listening for events of interest on a dgrid component +is very straightforward using `dojo/on`; furthermore, dgrid components possess +an `on` method, which is equivalent to calling `dojo/on` passing the +component's top-level DOM node as the target. + +Using the event delegation features of `dojo/on`, it is possible to listen for +all manner of events. For example, to listen for right-clicks on rows in the +grid's body: + +```js +grid.on(".dgrid-row:contextmenu", function(evt){ /* ... */ }); +``` + +Or, to listen to clicks on individual header cells: + +```js +grid.on(".dgrid-header .dgrid-cell:click", function(evt){ /* ... */ }); +``` + +In summary, pretty much any combination desired can be achieved by using +event delegation with selectors based on the `dgrid-header`, `dgrid-row`, and +`dgrid-cell` CSS classes as appropriate. + +## Obtaining information about events + +Commonly, the most important information about an event will be the row or cell +in which it was triggered. With dgrid components, this information is +retrievable by passing the event object itself to the instance's `row` or +`cell` method. These methods are particularly powerful, in that they are +capable of returning information about a row or cell, simply given a row/cell +element or any child thereof, or an event which originated from such an element. +These methods are also capable of looking up via a store item or ID; in the case +of `cell`, a second argument indicating column ID would also be passed. + +Expanding upon the examples above... + +```js +grid.on(".dgrid-row:contextmenu", function(evt){ + var row = grid.row(evt); + // row.element == the element with the dgrid-row class + // row.id == the identity of the item represented by the row + // row.data == the item represented by the row +}); + +grid.on(".dgrid-header .dgrid-cell:click", function(evt){ + var cell = grid.cell(evt); + // cell.element == the element with the dgrid-cell class + // cell.column == the column definition object for the column the cell is within + // cell.row == the same object obtained from grid.row(evt) +}); +``` + +## Custom events emitted by dgrid + +Event name|Emitted by +-----|----- +`dgrid-refresh-complete`|[`OnDemand*`](../components/core-components/OnDemandList-and-OnDemandGrid.md#events) and [`Pagination`](../components/extensions/Pagination.md#events) modules after grid has rendered (and after calling `grid.refresh`) +`dgrid-error`|[`OnDemand*`](../components/core-components/OnDemandList-and-OnDemandGrid.md#events) and [`Pagination`](../components/extensions/Pagination.md#events) modules when an error occurs +`dgrid-cellfocusin`
`dgrid-cellfocusout`|[`Keyboard`](../components/mixins/Keyboard.md#events) module when keyboard navigation occurs +`dgrid-sort`|[`Grid`](../components/core-components/Grid.md#events) module when a column is sorted +`dgrid-columnresize`|[`ColumnResizer`](../components/extensions/ColumnResizer.md#events) module +`dgrid-columnreorder`|[`ColumnReorder`](../components/extensions/ColumnReorder.md#events) module +`dgrid-columnstatechange`|[`ColumnHider`](../components/extensions/ColumnHider.md#events) module +`dgrid-datachange`|[`editor`](../components/column-plugins/editor.md#events) module when an editor field loses focus after being changed +`dgrid-editor-show`|[`editor`](../components/column-plugins/editor.md#events) module before placing a shared editor +`dgrid-editor-hide`|[`editor`](../components/column-plugins/editor.md#events) module before removing an `editOn` editor +`dgrid-select`|[`Selection`](../components/mixins/Selection.md#events) module when one or more rows are selected +`dgrid-deselect`|[`Selection`](../components/mixins/Selection.md#events) module when one or more rows are deselected \ No newline at end of file diff --git a/doc/usage/Working-with-Widgets.md b/doc/usage/Working-with-Widgets.md new file mode 100644 index 000000000..05a212ee2 --- /dev/null +++ b/doc/usage/Working-with-Widgets.md @@ -0,0 +1,40 @@ +# Working with Widgets + +## Layout widgets and the DijitRegistry extension + +When creating dgrid instances within Dijit layout widgets, it is recommended to +construct these instances with the DijitRegistry extension mixed in. +This enables functions like `registry.findWidgets` (also used by +`_WidgetBase.getChildren`) to report dgrid instances as well. + +**Note:** prior to version 0.3.5, dgrid components were unable to exist as direct +children of Dijit layout widgets; they can, however, exist as children of +ContentPanes. + +## Destroying rendered widgets + +When creating custom `renderRow` or `renderCell` functions which populate rows +or cells with new Dijit widget instances, it is important to keep in mind that +these widgets should ideally be maintained and destroyed when appropriate. + +While the first inclination might be to simply handle this in the list or grid +component's `destroy` method, it's important to realize that this will not cover +cases where rows are destroyed when scrolled out of view (for components like +OnDemandList and OnDemandGrid), resulting in leaked widget references. A more +appropriate time to destroy rendered widgets would be in `removeRow`, which is +called whenever a row is undrawn, including scroll operations in on-demand +components, as well as when `destroy` is called. + +For example, the [editor](../components/column-plugins/editor.md) column plugin performs cleanup in this fashion. +During initial rendering of an editor widget, a reference to the created widget +is stored, which is then accessed and destroyed when `removeRow` is called: + +```js +// add advice for cleaning up widgets in this column +aspect.before(grid, "removeRow", function(rowElement){ + // destroy our widget during the row removal operation + var cellElement = grid.cell(rowElement, column.id).element, + widget = (cellElement.contents || cellElement).widget; + if(widget){ widget.destroyRecursive(); } +}); +``` \ No newline at end of file