mirror of
https://github.com/pebble-dev/clay.git
synced 2026-08-28 04:46:57 -04:00
Merge pull request #54 from pebble/#52/custom-color-picker-layout
#52/custom color picker layout
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"rules": {
|
||||
"new-cap": [0],
|
||||
"max-params": [2, 4],
|
||||
"max-params": [2, 5],
|
||||
"require-jsdoc": [2, {
|
||||
"require": {
|
||||
"FunctionDeclaration": true,
|
||||
|
||||
@@ -292,11 +292,16 @@ A dropdown menu containing multiple options.
|
||||
|
||||
---
|
||||
|
||||
#### Color
|
||||
#### Color Picker
|
||||
|
||||
**Manipulator:** [`color`](#color)
|
||||
|
||||
A color picker containing the 64 supported colors on Basalt and Chalk.
|
||||
A color picker that allows users to choose a color from the ones that are compatible with their Pebble smartwatch.
|
||||
The color picker will automatically show a different layout depending on the watch connected:
|
||||
|
||||
- Aplite (Firmware 2.x) - Black and white
|
||||
- Aplite (Firmware 3.x) - Black and white. Will also include gray (`#AAAAAA`) if `allowGray` is set to `true`
|
||||
- Basalt/chalk - The 64 colors compatible with color Pebble smartwatches.
|
||||
|
||||
##### Properties
|
||||
|
||||
@@ -306,9 +311,11 @@ A color picker containing the 64 supported colors on Basalt and Chalk.
|
||||
| id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). |
|
||||
| appKey | string (unique) | The AppMessage key matching the `appKey` item defined in your `appinfo.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. |
|
||||
| label | string | The label that should appear next to this item. |
|
||||
| defaultValue | string OR int | The default color. Always use the uncorrected value even if `sunlight` is true. The component will do the conversion internally. |
|
||||
| defaultValue | string OR int | The default color. One of the [64 colors](https://developer.pebble.com/guides/tools-and-resources/color-picker/) compatible with Pebble smartwatches. Always use the uncorrected value even if `sunlight` is true. The component will do the conversion internally. |
|
||||
| description | string | Optional sub-text to include below the component |
|
||||
| sunlight | boolean | Use the color-corrected sunlight color palette if `true`, else the uncorrected version. Defaults to `true` if not specified. |
|
||||
| layout | string OR array | Optional. Use a custom layout for the color picker. Defaults to automatically choosing the most appropriate layout for the connected watch. The layout is represented by a two dimensional array. Use `false` to insert blank spaces. You may also use one of the preset layouts by setting `layout` to: `"COLOR"`, `"GRAY"` or `"BLACK_WHITE"` |
|
||||
| allowGray | boolean | Optional. Set this to `true` to include gray (`#AAAAAA`) in the color picker for aplite running on firmware 3 and above. This is optional because only a subset of the drawing operations support gray on aplite. Defaults to `false` |
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -316,9 +323,40 @@ A color picker containing the 64 supported colors on Basalt and Chalk.
|
||||
{
|
||||
"type": "color",
|
||||
"appKey": "background",
|
||||
"defaultValue": "FF0000",
|
||||
"defaultValue": "ff0000",
|
||||
"label": "Background Color",
|
||||
"sunlight": true
|
||||
"sunlight": true,
|
||||
"layout": [
|
||||
[false, "00aaff", false],
|
||||
["0055ff", "0000ff", "0000aa"],
|
||||
[false, "5555ff", false]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "color",
|
||||
"appKey": "background",
|
||||
"defaultValue": "ffffff",
|
||||
"label": "Background Color",
|
||||
"sunlight": false,
|
||||
"layout": "BLACK_WHITE"
|
||||
}
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "color",
|
||||
"appKey": "background",
|
||||
"defaultValue": "aaaaaa",
|
||||
"label": "Background Color",
|
||||
"sunlight": false,
|
||||
"allowGray": true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+223
-92
@@ -9,117 +9,225 @@ module.exports = {
|
||||
label: '',
|
||||
description: ''
|
||||
},
|
||||
initialize: function(minified) {
|
||||
initialize: function(minified, clay) {
|
||||
var HTML = minified.HTML;
|
||||
var self = this;
|
||||
var useSunlight = self.config.sunlight !== false;
|
||||
var sunlightColorMap = {
|
||||
'000000': '000000',
|
||||
'000055': '001e41',
|
||||
'0000aa': '004387',
|
||||
'0000ff': '0068ca',
|
||||
'005500': '2b4a2c',
|
||||
'005555': '27514f',
|
||||
'0055aa': '16638d',
|
||||
'0055ff': '007dce',
|
||||
'00aa00': '5e9860',
|
||||
'00aa55': '5c9b72',
|
||||
'00aaaa': '57a5a2',
|
||||
'00aaff': '4cb4db',
|
||||
'00ff00': '8ee391',
|
||||
'00ff55': '8ee69e',
|
||||
'00ffaa': '8aebc0',
|
||||
'00ffff': '84f5f1',
|
||||
'550000': '4a161b',
|
||||
'550055': '482748',
|
||||
'5500aa': '40488a',
|
||||
'5500ff': '2f6bcc',
|
||||
'555500': '564e36',
|
||||
'555555': '545454',
|
||||
'5555aa': '4f6790',
|
||||
'5555ff': '4180d0',
|
||||
'55aa00': '759a64',
|
||||
'55aa55': '759d76',
|
||||
'55aaaa': '71a6a4',
|
||||
'55aaff': '69b5dd',
|
||||
'55ff00': '9ee594',
|
||||
'55ff55': '9de7a0',
|
||||
'55ffaa': '9becc2',
|
||||
'55ffff': '95f6f2',
|
||||
'aa0000': '99353f',
|
||||
'aa0055': '983e5a',
|
||||
'aa00aa': '955694',
|
||||
'aa00ff': '8f74d2',
|
||||
'aa5500': '9d5b4d',
|
||||
'aa5555': '9d6064',
|
||||
'aa55aa': '9a7099',
|
||||
'aa55ff': '9587d5',
|
||||
'aaaa00': 'afa072',
|
||||
'aaaa55': 'aea382',
|
||||
'aaaaaa': 'ababab',
|
||||
'ffffff': 'ffffff',
|
||||
'aaaaff': 'a7bae2',
|
||||
'aaff00': 'c9e89d',
|
||||
'aaff55': 'c9eaa7',
|
||||
'aaffaa': 'c7f0c8',
|
||||
'aaffff': 'c3f9f7',
|
||||
'ff0000': 'e35462',
|
||||
'ff0055': 'e25874',
|
||||
'ff00aa': 'e16aa3',
|
||||
'ff00ff': 'de83dc',
|
||||
'ff5500': 'e66e6b',
|
||||
'ff5555': 'e6727c',
|
||||
'ff55aa': 'e37fa7',
|
||||
'ff55ff': 'e194df',
|
||||
'ffaa00': 'f1aa86',
|
||||
'ffaa55': 'f1ad93',
|
||||
'ffaaaa': 'efb5b8',
|
||||
'ffaaff': 'ecc3eb',
|
||||
'ffff00': 'ffeeab',
|
||||
'ffff55': 'fff1b5',
|
||||
'ffffaa': 'fff6d3'
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string|boolean|number} color
|
||||
* @returns {string}
|
||||
*/
|
||||
function cssColor(color) {
|
||||
if (color === false) { return 'transparent'; }
|
||||
if (typeof color === 'number') {
|
||||
color = color.toString(16);
|
||||
} else if (!color) {
|
||||
return 'transparent';
|
||||
}
|
||||
while (color.length < 6) {
|
||||
color = '0' + color;
|
||||
}
|
||||
|
||||
color = padColorString(color);
|
||||
|
||||
return '#' + (useSunlight ? sunlightColorMap[color] : color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} color
|
||||
* @return {string}
|
||||
*/
|
||||
function padColorString(color) {
|
||||
color = color.toLowerCase();
|
||||
|
||||
while (color.length < 6) {
|
||||
color = '0' + color;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number|string} value
|
||||
* @returns {string|*}
|
||||
*/
|
||||
function normalizeColor(value) {
|
||||
switch (typeof value) {
|
||||
case 'number': return padColorString(value.toString(16));
|
||||
case 'string': return value.replace(/^#|^0x/, '');
|
||||
default: return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array.<Array>} layout
|
||||
* @returns {Array}
|
||||
*/
|
||||
function flattenLayout(layout) {
|
||||
return layout.reduce(function(a, b) {
|
||||
return a.concat(b);
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HEX color to LAB.
|
||||
* Adapted from: https://github.com/antimatter15/rgb-lab
|
||||
* @param {string} hex
|
||||
* @returns {Array} - [l, a, b]
|
||||
*/
|
||||
function hex2lab(hex) {
|
||||
hex = hex.replace(/^#|^0x/, '');
|
||||
|
||||
var r = parseInt(hex.slice(0, 2), 16) / 255;
|
||||
var g = parseInt(hex.slice(2, 4), 16) / 255;
|
||||
var b = parseInt(hex.slice(4), 16) / 255;
|
||||
|
||||
r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
|
||||
g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
|
||||
b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
|
||||
|
||||
var x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
|
||||
var y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000;
|
||||
var z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
|
||||
|
||||
x = (x > 0.008856) ? Math.pow(x, 1 / 3) : (7.787 * x) + 16 / 116;
|
||||
y = (y > 0.008856) ? Math.pow(y, 1 / 3) : (7.787 * y) + 16 / 116;
|
||||
z = (z > 0.008856) ? Math.pow(z, 1 / 3) : (7.787 * z) + 16 / 116;
|
||||
|
||||
return [(116 * y) - 16, 500 * (x - y), 200 * (y - z)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the perceptual color distance between two LAB colors
|
||||
* @param {Array} labA
|
||||
* @param {Array} labB
|
||||
* @returns {number}
|
||||
*/
|
||||
function deltaE(labA, labB) {
|
||||
var deltaL = labA[0] - labB[0];
|
||||
var deltaA = labA[1] - labB[1];
|
||||
var deltaB = labA[2] - labB[2];
|
||||
|
||||
return Math.sqrt(Math.pow(deltaL, 2) +
|
||||
Math.pow(deltaA, 2) +
|
||||
Math.pow(deltaB, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout based on the connected watch
|
||||
* @returns {Array}
|
||||
*/
|
||||
function autoLayout() {
|
||||
if (!clay.meta.activeWatchInfo ||
|
||||
clay.meta.activeWatchInfo.firmware.major === 2 ||
|
||||
clay.meta.activeWatchInfo.platform === 'aplite' &&
|
||||
!self.config.allowGray) {
|
||||
return standardLayouts.BLACK_WHITE;
|
||||
}
|
||||
|
||||
if (clay.meta.activeWatchInfo.platform === 'aplite' && self.config.allowGray) {
|
||||
return standardLayouts.GRAY;
|
||||
}
|
||||
|
||||
return standardLayouts.COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number|string} color
|
||||
* @return {number}
|
||||
*/
|
||||
self.roundColorToLayout = function(color) {
|
||||
var itemValue = normalizeColor(color);
|
||||
|
||||
// if the color is not in the layout we will need find the closest match
|
||||
if (colorList.indexOf(itemValue) === -1) {
|
||||
var itemValueLAB = hex2lab(itemValue);
|
||||
var differenceArr = colorList.map(function(color) {
|
||||
var colorLAB = hex2lab(normalizeColor(color));
|
||||
return deltaE(itemValueLAB, colorLAB);
|
||||
});
|
||||
|
||||
// Get the lowest number from the differenceArray
|
||||
var lowest = Math.min.apply(Math, differenceArr);
|
||||
|
||||
// Get the index for that lowest number
|
||||
var index = differenceArr.indexOf(lowest);
|
||||
itemValue = colorList[index];
|
||||
}
|
||||
|
||||
return parseInt(itemValue, 16);
|
||||
};
|
||||
|
||||
var useSunlight = self.config.sunlight !== false;
|
||||
var sunlightColorMap = {
|
||||
'000000': '000000', '000055': '001e41', '0000aa': '004387', '0000ff': '0068ca',
|
||||
'005500': '2b4a2c', '005555': '27514f', '0055aa': '16638d', '0055ff': '007dce',
|
||||
'00aa00': '5e9860', '00aa55': '5c9b72', '00aaaa': '57a5a2', '00aaff': '4cb4db',
|
||||
'00ff00': '8ee391', '00ff55': '8ee69e', '00ffaa': '8aebc0', '00ffff': '84f5f1',
|
||||
'550000': '4a161b', '550055': '482748', '5500aa': '40488a', '5500ff': '2f6bcc',
|
||||
'555500': '564e36', '555555': '545454', '5555aa': '4f6790', '5555ff': '4180d0',
|
||||
'55aa00': '759a64', '55aa55': '759d76', '55aaaa': '71a6a4', '55aaff': '69b5dd',
|
||||
'55ff00': '9ee594', '55ff55': '9de7a0', '55ffaa': '9becc2', '55ffff': '95f6f2',
|
||||
'aa0000': '99353f', 'aa0055': '983e5a', 'aa00aa': '955694', 'aa00ff': '8f74d2',
|
||||
'aa5500': '9d5b4d', 'aa5555': '9d6064', 'aa55aa': '9a7099', 'aa55ff': '9587d5',
|
||||
'aaaa00': 'afa072', 'aaaa55': 'aea382', 'aaaaaa': 'ababab', 'ffffff': 'ffffff',
|
||||
'aaaaff': 'a7bae2', 'aaff00': 'c9e89d', 'aaff55': 'c9eaa7', 'aaffaa': 'c7f0c8',
|
||||
'aaffff': 'c3f9f7', 'ff0000': 'e35462', 'ff0055': 'e25874', 'ff00aa': 'e16aa3',
|
||||
'ff00ff': 'de83dc', 'ff5500': 'e66e6b', 'ff5555': 'e6727c', 'ff55aa': 'e37fa7',
|
||||
'ff55ff': 'e194df', 'ffaa00': 'f1aa86', 'ffaa55': 'f1ad93', 'ffaaaa': 'efb5b8',
|
||||
'ffaaff': 'ecc3eb', 'ffff00': 'ffeeab', 'ffff55': 'fff1b5', 'ffffaa': 'fff6d3'
|
||||
};
|
||||
|
||||
/* eslint-disable comma-spacing, no-multi-spaces, max-len,
|
||||
standard/array-bracket-even-spacing */
|
||||
var layout = self.config.layout || [
|
||||
[false , false , '55ff00', 'aaff55', false , 'ffff55', 'ffffaa', false , false ],
|
||||
[false , 'aaffaa', '55ff55', '00ff00', 'aaff00', 'ffff00', 'ffaa55', 'ffaaaa', false ],
|
||||
['55ffaa', '00ff55', '00aa00', '55aa00', 'aaaa55', 'aaaa00', 'ffaa00', 'ff5500', 'ff5555'],
|
||||
['aaffff', '00ffaa', '00aa55', '55aa55', '005500', '555500', 'aa5500', 'ff0000', 'ff0055'],
|
||||
[false , '55aaaa', '00aaaa', '005555', 'ffffff', '000000', 'aa5555', 'aa0000', false ],
|
||||
['55ffff', '00ffff', '00aaff', '0055aa', 'aaaaaa', '555555', '550000', 'aa0055', 'ff55aa'],
|
||||
['55aaff', '0055ff', '0000ff', '0000aa', '000055', '550055', 'aa00aa', 'ff00aa', 'ffaaff'],
|
||||
[false , '5555aa', '5555ff', '5500ff', '5500aa', 'aa00ff', 'ff00ff', 'ff55ff', false ],
|
||||
[false , false , false , 'aaaaff', 'aa55ff', 'aa55aa', false , false , false ]
|
||||
];
|
||||
standard/array-bracket-even-spacing */
|
||||
var standardLayouts = {
|
||||
COLOR: [
|
||||
[false , false , '55ff00', 'aaff55', false , 'ffff55', 'ffffaa', false , false ],
|
||||
[false , 'aaffaa', '55ff55', '00ff00', 'aaff00', 'ffff00', 'ffaa55', 'ffaaaa', false ],
|
||||
['55ffaa', '00ff55', '00aa00', '55aa00', 'aaaa55', 'aaaa00', 'ffaa00', 'ff5500', 'ff5555'],
|
||||
['aaffff', '00ffaa', '00aa55', '55aa55', '005500', '555500', 'aa5500', 'ff0000', 'ff0055'],
|
||||
[false , '55aaaa', '00aaaa', '005555', 'ffffff', '000000', 'aa5555', 'aa0000', false ],
|
||||
['55ffff', '00ffff', '00aaff', '0055aa', 'aaaaaa', '555555', '550000', 'aa0055', 'ff55aa'],
|
||||
['55aaff', '0055ff', '0000ff', '0000aa', '000055', '550055', 'aa00aa', 'ff00aa', 'ffaaff'],
|
||||
[false , '5555aa', '5555ff', '5500ff', '5500aa', 'aa00ff', 'ff00ff', 'ff55ff', false ],
|
||||
[false , false , false , 'aaaaff', 'aa55ff', 'aa55aa', false , false , false ]
|
||||
],
|
||||
GRAY: [
|
||||
['000000', 'aaaaaa', 'ffffff']
|
||||
],
|
||||
BLACK_WHITE: [
|
||||
['000000', 'ffffff']
|
||||
]
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
var layout = self.config.layout || autoLayout();
|
||||
|
||||
if (typeof layout === 'string') {
|
||||
layout = standardLayouts[layout];
|
||||
}
|
||||
|
||||
// make sure layout is a 2D array
|
||||
if (!Array.isArray(layout[0])) {
|
||||
layout = [layout];
|
||||
}
|
||||
|
||||
var colorList = flattenLayout(layout).map(function(item) {
|
||||
return normalizeColor(item);
|
||||
}).filter(function(item) {
|
||||
return item;
|
||||
});
|
||||
|
||||
var grid = '';
|
||||
var itemWidth = 100 / layout[0].length;
|
||||
var itemHeight = 100 / layout.length;
|
||||
var rows = layout.length;
|
||||
var cols = 0;
|
||||
layout.forEach(function(row) {
|
||||
cols = row.length > cols ? row.length : cols;
|
||||
});
|
||||
var itemWidth = 100 / cols;
|
||||
var itemHeight = 100 / rows;
|
||||
var $elem = self.$element;
|
||||
|
||||
for (var i = 0; i < layout.length; i++) {
|
||||
for (var j = 0; j < layout[i].length; j++) {
|
||||
for (var i = 0; i < rows; i++) {
|
||||
for (var j = 0; j < cols; j++) {
|
||||
|
||||
var color = layout[i][j];
|
||||
var color = normalizeColor(layout[i][j]);
|
||||
var selectable = (color ? ' selectable' : '');
|
||||
|
||||
var roundedTL = (i === 0 && j === 0) || i === 0 && !layout[i][j - 1] ||
|
||||
@@ -146,7 +254,7 @@ module.exports = {
|
||||
grid += '<i ' +
|
||||
'class="color-box ' + selectable + roundedTL + roundedTR + roundedBL +
|
||||
roundedBR + '" ' +
|
||||
'data-value="' + parseInt(color, 16) + '" ' +
|
||||
(color ? 'data-value="' + parseInt(color, 16) + '" ' : '') +
|
||||
'style="' +
|
||||
'width:' + itemWidth + '%; ' +
|
||||
'height:' + itemHeight + '%; ' +
|
||||
@@ -155,13 +263,34 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
$elem.select('.color-box-container').add(HTML(grid));
|
||||
// on very small layouts the boxes end up huge. The following adds extra padding
|
||||
// to prevent them from being so big.
|
||||
var extraPadding = 0;
|
||||
if (cols === 3) {
|
||||
extraPadding = 5;
|
||||
}
|
||||
if (cols === 2) {
|
||||
extraPadding = 8;
|
||||
}
|
||||
var vPadding = (extraPadding * itemWidth / itemHeight) + '%';
|
||||
var hPadding = extraPadding + '%';
|
||||
$elem.select('.color-box-container')
|
||||
.add(HTML(grid))
|
||||
.set('$paddingTop', vPadding)
|
||||
.set('$paddingRight', hPadding)
|
||||
.set('$paddingBottom', vPadding)
|
||||
.set('$paddingLeft', hPadding);
|
||||
|
||||
$elem.select('.color-box-wrap').set(
|
||||
'$paddingBottom',
|
||||
(itemWidth / itemHeight * 100) + '%'
|
||||
);
|
||||
|
||||
var $valueDisplay = $elem.select('.value');
|
||||
var $picker = $elem.select('.picker-wrap');
|
||||
var disabled = self.$manipulatorTarget.get('disabled');
|
||||
|
||||
$elem.select('label').on('click', function(ev) {
|
||||
$elem.select('label').on('click', function() {
|
||||
if (!disabled) {
|
||||
$picker.set('show'); // toggle visibility
|
||||
}
|
||||
@@ -191,5 +320,7 @@ module.exports = {
|
||||
disabled = false;
|
||||
});
|
||||
|
||||
// expose layout for testing
|
||||
self._layout = layout;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -160,16 +160,12 @@ module.exports = {
|
||||
},
|
||||
color: {
|
||||
get: function() {
|
||||
return parseInt(this.$manipulatorTarget.get('value'), 16);
|
||||
return parseInt(this.$manipulatorTarget.get('value'), 10) || 0;
|
||||
},
|
||||
set: function(value) {
|
||||
switch (typeof value) {
|
||||
case 'number': value = value.toString(16); break;
|
||||
case 'string': value = value.replace(/^#|^0x/, ''); break;
|
||||
}
|
||||
value = value || '000000';
|
||||
value = this.roundColorToLayout(value || 0);
|
||||
|
||||
if (this.get() === parseInt(value, 16)) { return this; }
|
||||
if (this.get() === value) { return this; }
|
||||
this.$manipulatorTarget.set('value', value);
|
||||
return this.trigger('change');
|
||||
},
|
||||
|
||||
@@ -113,6 +113,7 @@ label {
|
||||
|
||||
.tap-highlight {
|
||||
@include tap-highlight();
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
.component {
|
||||
|
||||
@@ -11,8 +11,6 @@ $item-spacing-v: $base-height / 2;
|
||||
$item-spacing-h: 0.75rem;
|
||||
$small-component-width: $base-height * $golden;
|
||||
|
||||
|
||||
|
||||
// ---- Colors ------
|
||||
$color-orange: #ff4700;
|
||||
$color-orange-dark: #993d19;
|
||||
@@ -37,4 +35,5 @@ $color-gray-11: #f2f2f2;
|
||||
$button-padding: 0.6rem;
|
||||
$button-padding-ios: 0.5rem;
|
||||
|
||||
$box-shadow-small-components: $color-gray-1 0 0.1rem 0.1rem;
|
||||
$box-shadow-small-components: 0 0.1rem 0.1rem $color-gray-1;
|
||||
$box-shadow-large-components: 0 rem(3px) rem(15px) rgba(0,0,0,0.4);
|
||||
|
||||
@@ -20,17 +20,25 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
padding: 1rem;
|
||||
background: rgba(0,0,0,0.7);
|
||||
padding: $item-spacing-v $item-spacing-h /2;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
opacity: 0;
|
||||
transition: opacity 70ms ease-in 175ms;
|
||||
transition: opacity 100ms ease-in 175ms;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.picker {
|
||||
padding: 1rem;
|
||||
background: $color-gray-11;
|
||||
padding: $item-spacing-v $item-spacing-h;
|
||||
background: $color-gray-4;
|
||||
box-shadow: $box-shadow-large-components;
|
||||
border-radius: $border-radius;
|
||||
width: 100%;
|
||||
max-width: 26rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&.show {
|
||||
@@ -46,7 +54,6 @@
|
||||
height: 0;
|
||||
width: 100%;
|
||||
padding: 0 0 100% 0; // overridden with inline style
|
||||
margin: 0.6em 0 0;
|
||||
|
||||
.color-box-container {
|
||||
position: absolute;
|
||||
@@ -77,7 +84,7 @@
|
||||
}
|
||||
|
||||
&.selected {
|
||||
transform: scale(1.15);
|
||||
transform: scale(1.1);
|
||||
border-radius: $border-radius;
|
||||
box-shadow: #111 0 0 0.24rem;
|
||||
position: relative;
|
||||
|
||||
+3
-2
@@ -95,14 +95,15 @@ module.exports.config = function(types, autoRegister) {
|
||||
* @param {boolean} [build=true] - run the build method on the result
|
||||
* @param {boolean} [autoRegister=true]
|
||||
* @param {Object} [settings] - settings to pass to constructor
|
||||
* @param {Object} [meta] - override the meta
|
||||
* @returns {ClayConfig}
|
||||
*/
|
||||
module.exports.clayConfig = function(types, build, autoRegister, settings) {
|
||||
module.exports.clayConfig = function(types, build, autoRegister, settings, meta) {
|
||||
var clayConfig = new ClayConfig(
|
||||
settings || {},
|
||||
module.exports.config(types, autoRegister),
|
||||
$(HTML('<div>')),
|
||||
module.exports.meta()
|
||||
module.exports.meta(meta)
|
||||
);
|
||||
return build === false ? clayConfig : clayConfig.build();
|
||||
};
|
||||
|
||||
@@ -12,6 +12,12 @@ module.exports = function(config) {
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['browserify', 'source-map-support', 'mocha'],
|
||||
|
||||
client: {
|
||||
mocha: {
|
||||
timeout: 10000 // 10 sec because Travis struggles sometimes.
|
||||
}
|
||||
},
|
||||
|
||||
browserify: {
|
||||
debug: true,
|
||||
transform: [
|
||||
|
||||
@@ -3,6 +3,48 @@
|
||||
var assert = require('chai').assert;
|
||||
var fixture = require('../../fixture');
|
||||
|
||||
var sunlightColorMap = {
|
||||
'000000': '000000', '000055': '001e41', '0000aa': '004387', '0000ff': '0068ca',
|
||||
'005500': '2b4a2c', '005555': '27514f', '0055aa': '16638d', '0055ff': '007dce',
|
||||
'00aa00': '5e9860', '00aa55': '5c9b72', '00aaaa': '57a5a2', '00aaff': '4cb4db',
|
||||
'00ff00': '8ee391', '00ff55': '8ee69e', '00ffaa': '8aebc0', '00ffff': '84f5f1',
|
||||
'550000': '4a161b', '550055': '482748', '5500aa': '40488a', '5500ff': '2f6bcc',
|
||||
'555500': '564e36', '555555': '545454', '5555aa': '4f6790', '5555ff': '4180d0',
|
||||
'55aa00': '759a64', '55aa55': '759d76', '55aaaa': '71a6a4', '55aaff': '69b5dd',
|
||||
'55ff00': '9ee594', '55ff55': '9de7a0', '55ffaa': '9becc2', '55ffff': '95f6f2',
|
||||
'aa0000': '99353f', 'aa0055': '983e5a', 'aa00aa': '955694', 'aa00ff': '8f74d2',
|
||||
'aa5500': '9d5b4d', 'aa5555': '9d6064', 'aa55aa': '9a7099', 'aa55ff': '9587d5',
|
||||
'aaaa00': 'afa072', 'aaaa55': 'aea382', 'aaaaaa': 'ababab', 'ffffff': 'ffffff',
|
||||
'aaaaff': 'a7bae2', 'aaff00': 'c9e89d', 'aaff55': 'c9eaa7', 'aaffaa': 'c7f0c8',
|
||||
'aaffff': 'c3f9f7', 'ff0000': 'e35462', 'ff0055': 'e25874', 'ff00aa': 'e16aa3',
|
||||
'ff00ff': 'de83dc', 'ff5500': 'e66e6b', 'ff5555': 'e6727c', 'ff55aa': 'e37fa7',
|
||||
'ff55ff': 'e194df', 'ffaa00': 'f1aa86', 'ffaa55': 'f1ad93', 'ffaaaa': 'efb5b8',
|
||||
'ffaaff': 'ecc3eb', 'ffff00': 'ffeeab', 'ffff55': 'fff1b5', 'ffffaa': 'fff6d3'
|
||||
};
|
||||
|
||||
/* eslint-disable comma-spacing, no-multi-spaces, max-len,
|
||||
standard/array-bracket-even-spacing */
|
||||
var standardLayouts = {
|
||||
COLOR: [
|
||||
[false , false , '55ff00', 'aaff55', false , 'ffff55', 'ffffaa', false , false ],
|
||||
[false , 'aaffaa', '55ff55', '00ff00', 'aaff00', 'ffff00', 'ffaa55', 'ffaaaa', false ],
|
||||
['55ffaa', '00ff55', '00aa00', '55aa00', 'aaaa55', 'aaaa00', 'ffaa00', 'ff5500', 'ff5555'],
|
||||
['aaffff', '00ffaa', '00aa55', '55aa55', '005500', '555500', 'aa5500', 'ff0000', 'ff0055'],
|
||||
[false , '55aaaa', '00aaaa', '005555', 'ffffff', '000000', 'aa5555', 'aa0000', false ],
|
||||
['55ffff', '00ffff', '00aaff', '0055aa', 'aaaaaa', '555555', '550000', 'aa0055', 'ff55aa'],
|
||||
['55aaff', '0055ff', '0000ff', '0000aa', '000055', '550055', 'aa00aa', 'ff00aa', 'ffaaff'],
|
||||
[false , '5555aa', '5555ff', '5500ff', '5500aa', 'aa00ff', 'ff00ff', 'ff55ff', false ],
|
||||
[false , false , false , 'aaaaff', 'aa55ff', 'aa55aa', false , false , false ]
|
||||
],
|
||||
GRAY: [
|
||||
['000000', 'aaaaaa', 'ffffff']
|
||||
],
|
||||
BLACK_WHITE: [
|
||||
['000000', 'ffffff']
|
||||
]
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
/**
|
||||
* @param {ClayItem} colorItem
|
||||
* @returns {void}
|
||||
@@ -11,6 +53,182 @@ function openPicker(colorItem) {
|
||||
colorItem.$element.select('label')[0].click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hex
|
||||
* @returns {string} - eg: "rgb(1, 2, 3)"
|
||||
*/
|
||||
function hexToRgb(hex) {
|
||||
var result = /^(?:#|0x)?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ?
|
||||
'rgb(' +
|
||||
parseInt(result[1], 16) + ', ' +
|
||||
parseInt(result[2], 16) + ', ' +
|
||||
parseInt(result[3], 16) + ')' :
|
||||
hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number|string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeColor(value) {
|
||||
switch (typeof value) {
|
||||
case 'number': value = value.toString(16); break;
|
||||
case 'string': value = value.replace(/^#|^0x/, ''); break;
|
||||
}
|
||||
return value || '000000';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array.<Array>} layout
|
||||
* @returns {Array}
|
||||
*/
|
||||
function flattenLayout(layout) {
|
||||
return layout.reduce(function(a, b) {
|
||||
return a.concat(b);
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array|string} [layout=color]
|
||||
* @param {Array} [expectedColors]
|
||||
* @return {void}
|
||||
*/
|
||||
function testCustomLayout(layout, expectedColors) {
|
||||
|
||||
describe('layout: ' + JSON.stringify(layout || 'default'), function() {
|
||||
|
||||
describe('Normal', function() {
|
||||
testColors(false, layout, expectedColors);
|
||||
});
|
||||
|
||||
describe('Sunlight', function() {
|
||||
testColors(true, layout, expectedColors);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} platform
|
||||
* @param {Array} layout
|
||||
* @param {boolean} allowGray
|
||||
* @param {string} desc
|
||||
* @param {Object} activeWatchInfo
|
||||
* @return {void}
|
||||
*/
|
||||
function testAutoLayout(platform, layout, allowGray, desc, activeWatchInfo) {
|
||||
it('chooses the best layout for the ' + (desc || platform) +
|
||||
' platform when allowGray is ' + allowGray,
|
||||
function() {
|
||||
var clayConfig = fixture.clayConfig(
|
||||
[{type: 'color', allowGray: allowGray}],
|
||||
true,
|
||||
true,
|
||||
{},
|
||||
{
|
||||
activeWatchInfo: activeWatchInfo
|
||||
}
|
||||
);
|
||||
var colorItem = clayConfig.getItemsByType('color')[0];
|
||||
assert.deepEqual(colorItem._layout, layout);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number|string} input
|
||||
* @param {number} expected
|
||||
* @param {array|string} layout
|
||||
* @return {void}
|
||||
*/
|
||||
function testClosestToLayout(input, expected, layout) {
|
||||
it('rounds ' + input + ' to ' + expected.toString(16) +
|
||||
' in layout: ' + JSON.stringify(layout),
|
||||
function() {
|
||||
var clayConfig;
|
||||
var colorItem;
|
||||
|
||||
clayConfig = fixture.clayConfig([{
|
||||
type: 'color',
|
||||
sunlight: false,
|
||||
layout: layout
|
||||
}]);
|
||||
colorItem = clayConfig.getItemsByType('color')[0];
|
||||
colorItem.set(input);
|
||||
assert.strictEqual(colorItem.get(), expected);
|
||||
|
||||
clayConfig = fixture.clayConfig([{
|
||||
type: 'color',
|
||||
sunlight: false,
|
||||
layout: layout
|
||||
}]);
|
||||
colorItem = clayConfig.getItemsByType('color')[0];
|
||||
colorItem.set(input);
|
||||
assert.strictEqual(colorItem.get(), expected);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} sunlight
|
||||
* @param {Array|string} [layout=color]
|
||||
* @param {Array} [expectedColors]
|
||||
* @return {void}
|
||||
*/
|
||||
function testColors(sunlight, layout, expectedColors) {
|
||||
var clayConfig;
|
||||
var colorItem;
|
||||
var $colorBoxes;
|
||||
var colors;
|
||||
|
||||
beforeEach(function() {
|
||||
clayConfig = fixture.clayConfig([
|
||||
{
|
||||
type: 'color',
|
||||
layout: layout,
|
||||
sunlight: sunlight
|
||||
}
|
||||
]);
|
||||
colorItem = clayConfig.getItemsByType('color')[0];
|
||||
|
||||
layout = layout || 'COLOR';
|
||||
if (typeof layout === 'string') {
|
||||
layout = standardLayouts[layout];
|
||||
}
|
||||
|
||||
colors = expectedColors || flattenLayout(layout);
|
||||
$colorBoxes = colorItem.$element.select('.color-box');
|
||||
});
|
||||
|
||||
it('Has the correct items in the layout', function() {
|
||||
colors.forEach(function(color, index) {
|
||||
if (color === false) {
|
||||
assert.strictEqual($colorBoxes[index].dataset.value, undefined);
|
||||
} else {
|
||||
assert.strictEqual(
|
||||
parseInt($colorBoxes[index].dataset.value, 10),
|
||||
parseInt(color || 0, 16)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the color correctly', function() {
|
||||
colors.forEach(function(color, index) {
|
||||
if (color === false) {
|
||||
assert.strictEqual(
|
||||
$colorBoxes[index].style.backgroundColor,
|
||||
'transparent'
|
||||
);
|
||||
} else {
|
||||
var normalizedColor = normalizeColor(color);
|
||||
assert.strictEqual(
|
||||
$colorBoxes[index].style.backgroundColor,
|
||||
hexToRgb(sunlight ? sunlightColorMap[normalizedColor] : normalizedColor)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('component - color', function() {
|
||||
it('shows the sunlit colors in the value display', function() {
|
||||
var clayConfig = fixture.clayConfig([
|
||||
@@ -105,4 +323,158 @@ describe('component - color', function() {
|
||||
$picker[0].click();
|
||||
assert.strictEqual($picker.get('classList').contains('show'), false);
|
||||
});
|
||||
|
||||
describe('color rounding', function() {
|
||||
var layout = 'GRAY';
|
||||
testClosestToLayout('000000', 0x000000, layout);
|
||||
testClosestToLayout('005500', 0x000000, layout);
|
||||
testClosestToLayout('55aa55', 0xaaaaaa, layout);
|
||||
testClosestToLayout('ffaaff', 0xaaaaaa, layout);
|
||||
testClosestToLayout('ffffaa', 0xffffff, layout);
|
||||
testClosestToLayout('ffffff', 0xffffff, layout);
|
||||
|
||||
layout = 'BLACK_WHITE';
|
||||
testClosestToLayout('000000', 0x000000, layout);
|
||||
testClosestToLayout('005500', 0x000000, layout);
|
||||
testClosestToLayout('55aa55', 0xffffff, layout);
|
||||
testClosestToLayout('ffaaff', 0xffffff, layout);
|
||||
testClosestToLayout('ffffaa', 0xffffff, layout);
|
||||
testClosestToLayout('ffffff', 0xffffff, layout);
|
||||
|
||||
layout = 'COLOR';
|
||||
testClosestToLayout('000000', 0x000000, layout);
|
||||
testClosestToLayout('005500', 0x005500, layout);
|
||||
testClosestToLayout('55bb55', 0x55aa55, layout);
|
||||
testClosestToLayout('ff99ff', 0xffaaff, layout);
|
||||
testClosestToLayout('ffffee', 0xffffff, layout);
|
||||
testClosestToLayout('ffffff', 0xffffff, layout);
|
||||
|
||||
layout = ['ff0000', 'ff5500', 'ff00ff', '00ff00', '555555'];
|
||||
testClosestToLayout('000000', 0x555555, layout);
|
||||
testClosestToLayout('ff0000', 0xff0000, layout);
|
||||
testClosestToLayout('ff1111', 0xff0000, layout);
|
||||
testClosestToLayout('00aa00', 0x00ff00, layout);
|
||||
testClosestToLayout('ff55ff', 0xff00ff, layout);
|
||||
testClosestToLayout('aaaaaa', 0x555555, layout);
|
||||
testClosestToLayout('ffffff', 0x555555, layout);
|
||||
});
|
||||
|
||||
describe('layouts', function() {
|
||||
|
||||
// allow gray does nothing for 2.x
|
||||
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, true, 'aplite (2.x)',
|
||||
null
|
||||
);
|
||||
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, true, 'aplite (2.x)', {
|
||||
platform: 'aplite',
|
||||
model: 'qemu_platform_aplite',
|
||||
language: 'en_US',
|
||||
firmware: {
|
||||
major: 2,
|
||||
minor: 10,
|
||||
patch: 0,
|
||||
suffix: ''
|
||||
}
|
||||
});
|
||||
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, false, 'aplite (2.x)',
|
||||
null
|
||||
);
|
||||
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, false, 'aplite (2.x)', {
|
||||
platform: 'aplite',
|
||||
model: 'qemu_platform_aplite',
|
||||
language: 'en_US',
|
||||
firmware: {
|
||||
major: 2,
|
||||
minor: 10,
|
||||
patch: 0,
|
||||
suffix: ''
|
||||
}
|
||||
});
|
||||
testAutoLayout('aplite', standardLayouts.BLACK_WHITE, false, 'aplite (3.x)', {
|
||||
platform: 'aplite',
|
||||
model: 'qemu_platform_aplite',
|
||||
language: 'en_US',
|
||||
firmware: {
|
||||
major: 3,
|
||||
minor: 10,
|
||||
patch: 0,
|
||||
suffix: ''
|
||||
}
|
||||
});
|
||||
testAutoLayout('aplite', standardLayouts.GRAY, true, 'aplite (3.x)', {
|
||||
platform: 'aplite',
|
||||
model: 'qemu_platform_aplite',
|
||||
language: 'en_US',
|
||||
firmware: {
|
||||
major: 3,
|
||||
minor: 10,
|
||||
patch: 0,
|
||||
suffix: ''
|
||||
}
|
||||
});
|
||||
testAutoLayout('basalt', standardLayouts.COLOR, true, '', {
|
||||
platform: 'basalt',
|
||||
model: 'qemu_platform_basalt',
|
||||
language: 'en_US',
|
||||
firmware: {
|
||||
major: 3,
|
||||
minor: 10,
|
||||
patch: 0,
|
||||
suffix: ''
|
||||
}
|
||||
});
|
||||
testAutoLayout('chalk', standardLayouts.COLOR, true, '', {
|
||||
platform: 'chalk',
|
||||
model: 'qemu_platform_chalk',
|
||||
language: 'en_US',
|
||||
firmware: {
|
||||
major: 3,
|
||||
minor: 10,
|
||||
patch: 0,
|
||||
suffix: ''
|
||||
}
|
||||
});
|
||||
|
||||
testCustomLayout();
|
||||
testCustomLayout('COLOR');
|
||||
testCustomLayout('GRAY');
|
||||
testCustomLayout('BLACK_WHITE');
|
||||
testCustomLayout(['aaffaa', '55ff55', '00ff00']);
|
||||
testCustomLayout([false, '55ff55', '00ff00']);
|
||||
testCustomLayout([['aaffaa', '55ff55', '00ff00']]);
|
||||
testCustomLayout(
|
||||
[['AAFFAA', '#55ff55', '0x00ff00', 0xff5500, null]],
|
||||
['aaffaa', '55ff55', '00ff00', 'ff5500', false]
|
||||
);
|
||||
testCustomLayout([
|
||||
['aaffaa', '55ff55', '00ff00'],
|
||||
['005555', 'ffffff', '000000']]
|
||||
);
|
||||
testCustomLayout([
|
||||
['aaffaa', '55ff55', '00ff00'],
|
||||
[false, 'ffffff', false]]
|
||||
);
|
||||
testCustomLayout(
|
||||
[
|
||||
['aaffaa', '55ff55', '00ff00'],
|
||||
[false, 'ffffff']
|
||||
],
|
||||
['aaffaa', '55ff55', '00ff00', false, 'ffffff', false]
|
||||
);
|
||||
testCustomLayout(
|
||||
[
|
||||
['aaffaa', '55ff55', false],
|
||||
[false, 'ffffff']
|
||||
],
|
||||
['aaffaa', '55ff55', false, false, 'ffffff', false]
|
||||
);
|
||||
testCustomLayout(
|
||||
[
|
||||
['aaffaa', '55ff55'],
|
||||
[false, 'ffffff', '000000']
|
||||
],
|
||||
['aaffaa', '55ff55', false, false, 'ffffff', '000000']
|
||||
);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,8 +62,6 @@ describe('Clay', function() {
|
||||
|
||||
it('handles the "showConfiguration" event if autoHandleEvents is not false',
|
||||
function() {
|
||||
this.timeout(10000); // 10 seconds
|
||||
|
||||
stubPebble();
|
||||
var clay = fixture.clay([]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user