/** * Sencha Pro Services presents xtype "colorselector". * API has been kept as close to the regular colorpicker as possible. The Selector can be * rendered to any container. * * The defaul selected color is configurable via {@link #value} config. Usually used in * forms via {@link Ext.ux.colorpick.Button} or {@link Ext.ux.colorpick.Field}. * * Typically you will need to listen for the change event to be notified when the user * chooses a color. Alternatively, you can bind to the "value" config * * @example * Ext.create('Ext.ux.colorpick.Selector', { * value: '993300', // initial selected color * renderTo: Ext.getBody(), * listeners: { * change: function (colorselector, color) { * console.log('New color: ' + color); * } * } * }); */Ext.define('Ext.ux.colorpick.Selector', { extend: 'Ext.container.Container', xtype: 'colorselector', mixins: [ 'Ext.ux.colorpick.Selection' ], controller: 'colorpick-selectorcontroller', requires: [ 'Ext.layout.container.HBox', 'Ext.form.field.Text', 'Ext.form.field.Number', 'Ext.ux.colorpick.ColorMap', 'Ext.ux.colorpick.SelectorModel', 'Ext.ux.colorpick.SelectorController', 'Ext.ux.colorpick.ColorPreview', 'Ext.ux.colorpick.Slider', 'Ext.ux.colorpick.SliderAlpha', 'Ext.ux.colorpick.SliderSaturation', 'Ext.ux.colorpick.SliderValue', 'Ext.ux.colorpick.SliderHue' ], config: { hexReadOnly: true }, width: 580, // default width and height gives 255x255 color map in Crisp height: 337, cls: Ext.baseCSSPrefix + 'colorpicker', padding: 10, layout: { type: 'hbox', align: 'stretch' }, defaultBindProperty: 'value', twoWayBindable: [ 'value' ], /** * @cfg fieldWidth {Number} Width of the text fields on the container (excluding HEX); * since the width of the slider containers is the same as the text field under it * (it's the same vbox column), changing this value will also affect the spacing between * the sliders. */ fieldWidth: 50, /** * @cfg fieldPad {Number} padding between the sliders and HEX/R/G/B fields. */ fieldPad: 5, /** * @cfg {Boolean} [showPreviousColor] * Whether "previous color" region (in upper right, below the selected color preview) * should be shown; these are relied upon by the {@link Ext.ux.colorpick.Button} and * the {@link Ext.ux.colorpick.Field}. */ showPreviousColor: false, /** * @cfg {Boolean} [showOkCancelButtons] * Whether Ok and Cancel buttons (in upper right, below the selected color preview) * should be shown; these are relied upon by the {@link Ext.ux.colorpick.Button} and * the {@link Ext.ux.colorpick.Field}. */ showOkCancelButtons: false, /** * @event change * Fires when a color is selected. Simply dragging sliders around will trigger this. * @param {Ext.ux.colorpick.Selector} this * @param {String} color The value of the selected color as per specified {@link #format}. * @param {String} previousColor The previous color value. */ /** * @event ok * Fires when OK button is clicked (see {@link #showOkCancelButtons}). * @param {Ext.ux.colorpick.Selector} this * @param {String} color The value of the selected color as per specified {@link #format}. */ /** * @event cancel * Fires when Cancel button is clicked (see {@link #showOkCancelButtons}). * @param {Ext.ux.colorpick.Selector} this */ listeners: { resize: 'onResize' }, constructor: function(config) { var me = this, childViewModel = Ext.Factory.viewModel('colorpick-selectormodel'); // Since this component needs to present its value as a thing to which users can // bind, we create an internal VM for our purposes. me.childViewModel = childViewModel; me.items = [ me.getMapAndHexRGBFields(childViewModel), me.getSliderAndHField(childViewModel), me.getSliderAndSField(childViewModel), me.getSliderAndVField(childViewModel), me.getSliderAndAField(childViewModel), me.getPreviewAndButtons(childViewModel, config) ]; me.childViewModel.bind('{selectedColor}', function(color) { me.setColor(color); }); me.callParent([config]); }, updateColor: function(color) { var me = this; me.mixins.colorselection.updateColor.call(me, color); me.childViewModel.set('selectedColor', color); }, updatePreviousColor: function(color) { this.childViewModel.set('previousColor', color); }, // Splits up view declaration for readability // "Map" and HEX/R/G/B fields getMapAndHexRGBFields: function(childViewModel) { var me = this, fieldMargin = { top: 0, right: me.fieldPad, bottom: 0, left: 0 }, fieldWidth = me.fieldWidth; return { xtype: 'container', viewModel: childViewModel, cls: Ext.baseCSSPrefix + 'colorpicker-escape-overflow', flex: 1, layout: { type: 'vbox', align: 'stretch' }, margin: '0 10 0 0', items: [ // "MAP" { xtype: 'colorpickercolormap', reference: 'colorMap', flex: 1, bind: { position: { bindTo: '{selectedColor}', deep: true }, hue: '{selectedColor.h}' }, listeners: { handledrag: 'onColorMapHandleDrag' } }, // HEX/R/G/B FIELDS { xtype: 'container', layout: 'hbox', defaults: { labelAlign: 'top', labelSeparator: '', allowBlank: false, onChange: function() { // prevent data binding propagation if bad value if (this.isValid()) { // this is kind of dirty and ideally we would extend these fields // and override the method, but works for now Ext.form.field.Base.prototype.onChange.apply(this, arguments); } } }, items: [{ xtype: 'textfield', fieldLabel: 'HEX', flex: 1, bind: '{hex}', margin: fieldMargin, regex: /^#[0-9a-f]{6}$/i, readonly: me.getHexReadOnly() }, { xtype: 'numberfield', fieldLabel: 'R', bind: '{red}', width: fieldWidth, hideTrigger: true, maxValue: 255, minValue: 0, margin: fieldMargin }, { xtype: 'numberfield', fieldLabel: 'G', bind: '{green}', width: fieldWidth, hideTrigger: true, maxValue: 255, minValue: 0, margin: fieldMargin }, { xtype: 'numberfield', fieldLabel: 'B', bind: '{blue}', width: fieldWidth, hideTrigger: true, maxValue: 255, minValue: 0, margin: 0 }] } ] }; }, // Splits up view declaration for readability // Slider and H field getSliderAndHField: function(childViewModel) { var me = this, fieldWidth = me.fieldWidth; return { xtype: 'container', viewModel: childViewModel, cls: Ext.baseCSSPrefix + 'colorpicker-escape-overflow', width: fieldWidth, layout: { type: 'vbox', align: 'stretch' }, items: [ { xtype: 'colorpickersliderhue', reference: 'hueSlider', flex: 1, bind: { hue: '{selectedColor.h}' }, width: fieldWidth, listeners: { handledrag: 'onHueSliderHandleDrag' } }, { xtype: 'numberfield', fieldLabel: 'H', labelAlign: 'top', labelSeparator: '', bind: '{hue}', hideTrigger: true, maxValue: 360, minValue: 0, allowBlank: false, margin: 0 } ] }; }, // Splits up view declaration for readability // Slider and S field getSliderAndSField: function(childViewModel) { var me = this, fieldWidth = me.fieldWidth; return { xtype: 'container', viewModel: childViewModel, cls: Ext.baseCSSPrefix + 'colorpicker-escape-overflow', width: fieldWidth, layout: { type: 'vbox', align: 'stretch' }, margin: { right: me.fieldPad, left: me.fieldPad }, items: [ { xtype: 'colorpickerslidersaturation', reference: 'satSlider', flex: 1, bind: { saturation: '{saturation}', hue: '{selectedColor.h}' }, width: fieldWidth, listeners: { handledrag: 'onSaturationSliderHandleDrag' } }, { xtype: 'numberfield', fieldLabel: 'S', labelAlign: 'top', labelSeparator: '', bind: '{saturation}', hideTrigger: true, maxValue: 100, minValue: 0, allowBlank: false, margin: 0 } ] }; }, // Splits up view declaration for readability // Slider and V field getSliderAndVField: function(childViewModel) { var me = this, fieldWidth = me.fieldWidth; return { xtype: 'container', viewModel: childViewModel, cls: Ext.baseCSSPrefix + 'colorpicker-escape-overflow', width: fieldWidth, layout: { type: 'vbox', align: 'stretch' }, items: [ { xtype: 'colorpickerslidervalue', reference: 'valueSlider', flex: 1, bind: { value: '{value}', hue: '{selectedColor.h}' }, width: fieldWidth, listeners: { handledrag: 'onValueSliderHandleDrag' } }, { xtype: 'numberfield', fieldLabel: 'V', labelAlign: 'top', labelSeparator: '', bind: '{value}', hideTrigger: true, maxValue: 100, minValue: 0, allowBlank: false, margin: 0 } ] }; }, // Splits up view declaration for readability // Slider and A field getSliderAndAField: function(childViewModel) { var me = this, fieldWidth = me.fieldWidth; return { xtype: 'container', viewModel: childViewModel, cls: Ext.baseCSSPrefix + 'colorpicker-escape-overflow', width: fieldWidth, layout: { type: 'vbox', align: 'stretch' }, margin: { left: me.fieldPad }, items: [ { xtype: 'colorpickerslideralpha', reference: 'alphaSlider', flex: 1, bind: { alpha: '{alpha}', color: { bindTo: '{selectedColor}', deep: true } }, width: fieldWidth, listeners: { handledrag: 'onAlphaSliderHandleDrag' } }, { xtype: 'numberfield', fieldLabel: 'A', labelAlign: 'top', labelSeparator: '', bind: '{alpha}', hideTrigger: true, maxValue: 100, minValue: 0, allowBlank: false, margin: 0 } ] }; }, // Splits up view declaration for readability // Preview current/previous color squares and OK and Cancel buttons getPreviewAndButtons: function(childViewModel, config) { // selected color preview is always shown var items = [{ xtype: 'colorpickercolorpreview', flex: 1, bind: { color: { bindTo: '{selectedColor}', deep: true } } }]; // previous color preview is optional if (config.showPreviousColor) { items.push({ xtype: 'colorpickercolorpreview', flex: 1, bind: { color: { bindTo: '{previousColor}', deep: true } }, listeners: { click: 'onPreviousColorSelected' } }); } // Ok/Cancel buttons are optional if (config.showOkCancelButtons) { items.push({ xtype: 'button', text: 'OK', margin: '10 0 0 0', padding: '10 0 10 0', handler: 'onOK' }, { xtype: 'button', text: 'Cancel', margin: '10 0 0 0', padding: '10 0 10 0', handler: 'onCancel' }); } return { xtype: 'container', viewModel: childViewModel, width: 70, margin: '0 0 0 10', items: items, layout: { type: 'vbox', align: 'stretch' } }; }});