diff --git a/README.md b/README.md index 8ba45bc..72bb2c3 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ + +# The repository has been moved to: https://github.com/sproutcore/sproutcore-table +------ + #Overview This is a table view for [Sproutcore](http://www.github.com/sproutcore/sproutcore). diff --git a/views/table_row.js b/views/table_row.js index ca8027b..cc6e23d 100755 --- a/views/table_row.js +++ b/views/table_row.js @@ -33,7 +33,7 @@ SC.TableRowView = SC.ListItemView.extend({ return this.getPath('displayDelegate.tableDelegate'); }.property('displayDelegate').cacheable(), - + render: function(context) { var tableDelegate = this.get('tableDelegate'), left = 3, @@ -61,8 +61,8 @@ SC.TableRowView = SC.ListItemView.extend({ context = context.push('
'); if (contentCheckboxKey && contentCheckboxKey.contains(key)) { - var value = SC.get(content, key) || false; - this.renderCheckbox(context, value); + var value = SC.getPath(content, key) || false; + this.renderCheckbox(context, value, key); } else { tableDelegate.renderTableCellContent(this, context, content, contentIndex, col, key); @@ -72,6 +72,7 @@ SC.TableRowView = SC.ListItemView.extend({ left += width; } + }, @@ -105,8 +106,8 @@ SC.TableRowView = SC.ListItemView.extend({ $cell.css({ width: width+'px', left: left+'px', }); if (contentCheckboxKey && contentCheckboxKey.contains(key)) { - var value = SC.get(content, key) || false; - this.updateCheckbox($cell, value); + var value = SC.getPath(content, key) || false; + this.updateCheckbox($cell, value, key); } else { tableDelegate.updateTableCellContent(this, $cell, content, contentIndex, col, key); @@ -132,9 +133,12 @@ SC.TableRowView = SC.ListItemView.extend({ var content = this.get('content'); // There may be a memory leak in SC with _checkboxRenderSource - if (this._checkboxRenderSource) { - this._checkboxRenderSource.destroy(); - this._checkboxRenderSource = this._checkboxRenderDelegate = null; + var sources = this._tr_cbSources; + if (sources) { + this.get('contentCheckboxKey').forEach(function(k) { + sources[k].destroy(); + }); + this._tr_cbSources = this._checkboxRenderDelegate = null; } this.updateContentObservers(null, content); @@ -214,27 +218,217 @@ SC.TableRowView = SC.ListItemView.extend({ // CHECKBOX EDTITING // - updateCheckbox: function (jQuery, state) { + /** @private + Adds a checkbox with the appropriate state to the content. This method + will only be called if the list item view is supposed to have a + checkbox. + + @param {SC.RenderContext} context the render context + @param {Boolean} state YES, NO or SC.MIXED_STATE + @returns {void} + */ + renderCheckbox: function (context, state, key) { var renderer = this.get('theme').checkboxRenderDelegate; - var source = this._checkboxRenderSource; + // note: checkbox-view is really not the best thing to do here; we should do + // sc-list-item-checkbox; however, themes expect something different, unfortunately. + context = context.begin('div') + .addClass('sc-checkbox-view') + .addClass('sc-checkbox-view-for-' + key) + .addClass(this.get('theme').classNames) + .addClass(renderer.get('className')); + + var sources = this._tr_cbSources; + if (!sources) { + sources = this._tr_cbSources = {}; + } + + var source = sources[key]; if (!source) { - source = this._checkboxRenderSource = - SC.Object.create({ renderState: {}, theme: this.get('theme') }); + source = sources[key] = SC.Object.create({ + renderState: {}, + theme: this.get('theme') + }); } + var editableKeys = this.get('editableKeys'); + var keyInEditableKeys = editableKeys && editableKeys.indexOf(key) !== -1; + source .set('controlSize', SC.SMALL_CONTROL_SIZE) .set('isSelected', state && (state !== SC.MIXED_STATE)) - .set('isEnabled', this.get('isEnabled') && this.get('contentIsEditable')) + .set('isEnabled', this.get('isEnabled') && this.get('contentIsEditable') && keyInEditableKeys) .set('isActive', this._checkboxIsActive) .set('title', ''); - renderer.update(source, jQuery.find('.sc-checkbox-view')); + renderer.render(source, context); + context = context.end(); this._checkboxRenderDelegate = renderer; }, + + updateCheckbox: function (jQuery, state, key) { + var renderer = this.get('theme').checkboxRenderDelegate; + + var sources = this._tr_cbSources; + var source = sources[key]; + + var editableKeys = this.get('editableKeys'); + var keyInEditableKeys = editableKeys && editableKeys.indexOf(key) !== -1; + + source + .set('controlSize', SC.SMALL_CONTROL_SIZE) + .set('isSelected', state && (state !== SC.MIXED_STATE)) + .set('isEnabled', this.get('isEnabled') && this.get('contentIsEditable') && keyInEditableKeys) + .set('isActive', this._checkboxIsActive) + .set('title', ''); + + renderer.update(source, jQuery.find('.sc-checkbox-view-for-' + key)); + + this._checkboxRenderDelegate = renderer; + }, + + _isInsideCheckbox: function (evt) { + var del = this.displayDelegate; + var checkboxKey = this.getDelegateProperty('contentCheckboxKey', del); + // should be an array as it is set that way through render and update + + return checkboxKey && checkboxKey.find(function (k) { + return this._isInsideElementWithClassName('sc-checkbox-view-for-' + k, evt); + }, this); + }, + + _addCheckboxActiveState: function (key) { + if (this.get('isEnabled') && this.get('editableKeys') && this.get('editableKeys').contains(key)) { + if (this._checkboxRenderDelegate) { + var sources = this._tr_cbSources; + var source = sources[key]; + + source.set('isActive', YES); + + this._checkboxRenderDelegate.update(source, this.$('.sc-checkbox-view-for-' + key)); + } else { + // for backwards-compatibility. + this.$('.sc-checkbox-view-for-' + key).addClass('active'); + } + } + }, + + _removeCheckboxActiveState: function () { + var key = this._isMouseDownOnCheckboxFor; + if (this._checkboxRenderer) { + var sources = this._tr_cbSources; + var source = sources[key]; + + source.set('isActive', NO); + + this._checkboxRenderDelegate.update(source, this.$('.sc-checkbox-view-for-' + key)); + } else { + // for backwards-compatibility. + this.$('.sc-checkbox-view-for-' + key).removeClass('active'); + } + }, + + + /** @private + mouseDown is handled only for clicks on the checkbox view or or action + button. + */ + mouseDown: function (evt) { + // Fast path, reject secondary clicks. + if (evt.which && evt.which !== 1) return false; + + // if content is not editable, then always let collection view handle the + // event. + if (!this.get('contentIsEditable')) return NO; + + // if occurred inside checkbox, item view should handle the event. + var inCheckboxForKey = this._isInsideCheckbox(evt); + if (inCheckboxForKey) { + this._addCheckboxActiveState(inCheckboxForKey); + this._isMouseDownOnCheckbox = YES; + this._isMouseInsideCheckbox = YES; + this._isMouseDownOnCheckboxFor = inCheckboxForKey; + return YES; // listItem should handle this event + } else if (this._isInsideDisclosure(evt)) { + this._addDisclosureActiveState(); + this._isMouseDownOnDisclosure = YES; + this._isMouseInsideDisclosure = YES; + return YES; + } else if (this._isInsideRightIcon(evt)) { + this._addRightIconActiveState(); + this._isMouseDownOnRightIcon = YES; + this._isMouseInsideRightIcon = YES; + return YES; + } + this.displayDidChange(); + return NO; // let the collection view handle this event + }, + + /** @private */ + mouseUp: function (evt) { + var ret = NO; + + // if mouse was down in checkbox -- then handle mouse up, otherwise + // allow parent view to handle event. + if (this._isMouseDownOnCheckbox) { + // update only if mouse inside on mouse up... + var inCheckboxForKey = this._isInsideCheckbox(evt); + if (inCheckboxForKey && inCheckboxForKey === this._isMouseDownOnCheckboxFor) { + if (this.get('editableKeys') && this.get('editableKeys').contains(inCheckboxForKey)) { + this.toggleCheckbox(inCheckboxForKey); + } + } + + this._removeCheckboxActiveState(); + ret = YES; + + // if mouse as down on disclosure -- handle mouse up. otherwise pass on + // to parent. + } else if (this._isMouseDownOnDisclosure) { + if (this._isInsideDisclosure(evt)) { + this.toggleDisclosure(); + } + + this._removeDisclosureActiveState(); + ret = YES; + // if mouse was down in right icon -- then handle mouse up, otherwise + // allow parent view to handle event. + } else if (this._isMouseDownOnRightIcon) { + this._removeRightIconActiveState(); + ret = YES; + } + + // clear cached info + this._isMouseInsideCheckbox = this._isMouseDownOnCheckbox = NO; + this._isMouseDownOnDisclosure = this._isMouseInsideDisclosure = NO; + this._isMouseInsideRightIcon = this._isMouseDownOnRightIcon = NO; + // this._isMouseDownOnCheckboxFor = null; + + return ret; + }, + + /** @private */ + mouseMoved: function (evt) { + return NO; + }, + + toggleCheckbox: function (key) { + var content = this.get('content'); + //del = this.displayDelegate; + + if (content && content.get) { + var value = content.get(key); + + value = (value === SC.MIXED_STATE) ? YES : !value; + content.set(key, value); // update content + + this.displayDidChange(); // repaint view... + } + + } + });