From 54de02149bd8504175a34229c6b1b35b81da8ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Thu, 2 Oct 2014 16:05:56 +0200 Subject: [PATCH] Implement 'row' type component, for multiple components per row in palettes. --- src/basic/Rectangle.js | 1 - src/dom/DomElement.js | 5 ++ src/ui/Component.js | 111 +++++++++++++++++++++++++++-------------- src/ui/Palette.js | 6 ++- src/ui/Pane.js | 64 ++++++++++++++---------- 5 files changed, 120 insertions(+), 67 deletions(-) diff --git a/src/basic/Rectangle.js b/src/basic/Rectangle.js index c225d88a..1170ba30 100644 --- a/src/basic/Rectangle.js +++ b/src/basic/Rectangle.js @@ -836,7 +836,6 @@ var Rectangle = Base.extend(/** @lends Rectangle# */{ * @class An internal version of Rectangle that notifies its owner of each * change through setting itself again on the setter that corresponds to the * getter that produced this LinkedRectangle. - * See uses of LinkedRectangle.create() * Note: This prototype is not exported. * * @private diff --git a/src/dom/DomElement.js b/src/dom/DomElement.js index 8c018a01..00d8e6cf 100644 --- a/src/dom/DomElement.js +++ b/src/dom/DomElement.js @@ -159,6 +159,11 @@ var DomElement = new function() { el.parentNode.removeChild(el); }, + addChildren: function(el, children) { + // We can use the create() function for this too! + return create(children, el); + }, + removeChildren: function(el) { while (el.firstChild) el.removeChild(el.firstChild); diff --git a/src/ui/Component.js b/src/ui/Component.js index 9bf7105e..09b29908 100644 --- a/src/ui/Component.js +++ b/src/ui/Component.js @@ -78,60 +78,91 @@ var Component = Base.extend(Callback, /** @lends Component# */{ return new Color(value).toCSS( DomElement.get(this._input, 'type') === 'color'); } - } + }, + + row: {} }, // Default values for internals + _visible: true, _enabled: true, - initialize: function Component(obj) { + initialize: function Component(pane, name, props, value, row, parent) { + if (value === undefined) + value = props.value; + if (!name) + name = 'component-' + this._id, this._id = Component._id = (Component._id || 0) + 1; - var type = this._type = obj.type in this._types - ? obj.type - : 'options' in obj - ? 'list' - : 'onClick' in obj - ? 'button' - : typeof obj.value, + this._pane = pane; + this._name = name; + this._row = row; + this._parent = parent; // The parent component, if any. + var type = this._type = props.type in this._types + ? props.type + : 'options' in props + ? 'list' + : 'onClick' in props + ? 'button' + : typeof value, meta = this._meta = this._types[type] || { type: type }, - name = this._name = obj.name || 'component-' + this._id, - that = this; - this._input = DomElement.create(meta.tag || 'input', { - id: 'palettejs-input-' + name, - type: meta.type, - events: { - change: function() { - that.setValue( - DomElement.get(this, meta.value || 'value')); - }, - click: function() { - that.fire('click'); - } + that = this, + create = DomElement.create; + if (type === 'row') { + var components = this._components = []; + for (var key in props) { + var entry = props[key]; + if (Base.isPlainObject(entry)) + components.push(new Component(pane, key, entry, + pane._values[key], row, this)); } - }); + pane._maxComponents = Math.max(components.length, + pane._maxComponents || 0); + } else { + DomElement.addChildren(row, [ + this._labelCell = create('td', { + class: 'palettejs-label', + id: 'palettejs-label-' + name + }), + this._inputCell = create('td', { + class: 'palettejs-input', + id: 'palettejs-input-' + name + }, [ + this._input = create(meta.tag || 'input', { + type: meta.type, + events: { + change: function() { + that.setValue(DomElement.get(this, + meta.value || 'value')); + }, + click: function() { + that.fire('click'); + } + } + }) + ]) + ]); + } // Attach default 'change' even that delegates to palette this.attach('change', function(value) { if (!this._dontFire) - this._palette.fire('change', this, this._name, value); + pane.fire('change', this, this._name, value); }); - this._element = DomElement.create('tr', - { class: 'palettejs-row', id: 'palettejs-row-' + name }, [ - this._labelCell = DomElement.create('td', - { class: 'palettejs-label' }), - 'td', { class: 'palettejs-input' }, [this._input] - ]); this._dontFire = true; - // Now that everything is set up, copy over values fro obj. + // Now that everything is set up, copy over values fro, props. // NOTE: This triggers setters, which is why we set _dontFire = true, // and why we can only call this after everything else is set up (e.g. // setLabel() requires this._labelCell). // Exclude name because it's already set, and value since we want to set // it after range. - Base.set(this, obj, { name: true, value: true }); - this.setValue(obj.value); - this._defaultValue = this._value; + Base.set(this, props, { name: true, value: true }); + this.setValue(value); // Start firing change events after we have initialized. this._dontFire = false; + // Store link to component in the pane's components object. + pane._components[name] = this; + // Make sure each component has an entry in values also, so observers + // get installed correctly in the Pane constructor. + pane._values[name] = this._defaultValue = this._value; }, getType: function() { @@ -161,7 +192,7 @@ var Component = Base.extend(Callback, /** @lends Component# */{ setSuffix: function(suffix) { this._suffix = suffix; DomElement.set(this._suffixNode = this._suffixNode - || this._input.parentNode.appendChild(DomElement.create('label', + || this._inputCell.appendChild(DomElement.create('label', { 'for': 'palettejs-input-' + this._name })), 'text', suffix); }, @@ -202,11 +233,15 @@ var Component = Base.extend(Callback, /** @lends Component# */{ }, getVisible: function() { - return !DomElement.hasClass(this._element, 'hidden'); + return this._visible; }, setVisible: function(visible) { - DomElement.toggleClass(this._element, 'hidden', !visible); + // NOTE: Only set the visibility of the whole row if this is a row item, + // in which case this._input is not defined. + DomElement.toggleClass(this._inputCell || this._row, 'hidden', !visible); + DomElement.toggleClass(this._labelCell, 'hidden', !visible); + this._visible = !!visible; }, getEnabled: function() { @@ -223,7 +258,7 @@ var Component = Base.extend(Callback, /** @lends Component# */{ enabled = enabled && prev; } DomElement.set(this._input, 'disabled', !enabled); - this._enabled = enabled; + this._enabled = !!enabled; }, getRange: function() { diff --git a/src/ui/Palette.js b/src/ui/Palette.js index e6c14736..f56c8baa 100644 --- a/src/ui/Palette.js +++ b/src/ui/Palette.js @@ -25,11 +25,13 @@ // DOCS: Palette#remove() initialize: function Palette(title, components, values) { - Pane.call(this, title, components, values, 'palettejs-palette'); + Pane.call(this, title, components, values); var parent = DomElement.find('.palettejs-panel') || DomElement.find('body').appendChild( DomElement.create('div', { class: 'palettejs-panel' })); - parent.appendChild(this._element); + this._element = parent.appendChild( + DomElement.create('div', { class: 'palettejs-palette' }, + [this._table])); // Link to the current scope's palettes list. // TODO: This is the only paper dependency in Palette.js // Find a way to make it independent. diff --git a/src/ui/Pane.js b/src/ui/Pane.js index 12d11dd5..eb7300af 100644 --- a/src/ui/Pane.js +++ b/src/ui/Pane.js @@ -20,7 +20,7 @@ var Pane = Base.extend(Callback, /** @lends Pane# */{ // Defaults for internals _enabled: true, - initialize: function Pane(title, components, values, className) { + initialize: function Pane(title, components, values) { // Support object literal constructor var props = Base.isPlainObject(title) && title; if (props) { @@ -28,38 +28,42 @@ var Pane = Base.extend(Callback, /** @lends Pane# */{ components = props.components; values = props.values; } - this._element = DomElement.create('table', { - class: 'palettejs-pane' + (className ? ' ' + className : '') - }); - this._title = title; if (!values) values = {}; - for (var name in (this.components = components)) { - var component = components[name]; - if (!(component instanceof Component)) { - component = components[name] = new Component( - new Base(component, { - value: Base.pick(component.value, values[name]), - name: name - })); + this._table = DomElement.create('table', { class: 'palettejs-pane' }); + this._title = title; + // NOTE: We modify the actual passed components and values objects so + // the newly created components and their values can easily be + // referenced from outside. + this._components = components; + this._values = values; + this._maxComponents = 1; // 1 component per row is the default. + for (var name in components) { + var row = DomElement.create('tr', { class: 'palettejs-row' }), + component = new Component(this, name, components[name], + values[name], row); + DomElement.set(row, 'id', 'palettejs-row-' + component._name); + this._table.appendChild(row); + } + if (this._maxComponents > 1) { + for (name in components) { + var component = components[name]; + if (component._inputCell && !component._parent) { + DomElement.set(component._inputCell, 'colspan', + this._maxComponents * 2 - 1); + } } - this._element.appendChild(component._element); - component._palette = this; - // Make sure each component has an entry in values, so observers get - // installed further down. - if (values[name] === undefined) - values[name] = component._value; } // Now replace each entry in values with a getter / setters so we can // directly link the value to the component and observe change. - this.values = Base.each(values, function(value, name) { - var component = components[name]; + Base.each(values, function(value, name) { + var component = components && components[name]; if (component) { Base.define(values, name, { enumerable: true, configurable: true, get: function() { - return component._value; + return component.getValue(); }, set: function(val) { component.setValue(val); @@ -73,14 +77,22 @@ var Pane = Base.extend(Callback, /** @lends Pane# */{ } }, + getComponents: function() { + return this._components; + }, + + getValues: function() { + return this._values; + }, + getEnabled: function() { return this._enabled; }, setEnabled: function(enabled) { this._enabled = enabled; - for (var i in this.components) - this.components[i].setEnabled(enabled, true); + for (var i in this._components) + this._components[i].setEnabled(enabled, true); }, /** @@ -88,7 +100,7 @@ var Pane = Base.extend(Callback, /** @lends Pane# */{ * {@link Component#defaultValue}. */ reset: function() { - for (var i in this.components) - this.components[i].reset(); + for (var i in this._components) + this._components[i].reset(); } });