Merge + palette manager 1st drop

- can create palettes
- palettes are persisted to local storage
- can add colors to palettes using spectrum color picker
- can remove created palettes
- can revert changes on unsaved palettes

Merge branch 'master' into feature-color-palette

Conflicts:
	src/css/spectrum/spectrum-overrides.css
This commit is contained in:
jdescottes 2014-03-29 18:35:56 +01:00
commit f2da622edb
16 changed files with 661 additions and 31 deletions

View File

@ -1,18 +1,19 @@
#dialog-container-wrapper {
position: absolute;
z-index: 10000;
z-index: 20000;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 50px;
padding: 50px 150px;
box-sizing: border-box;
-moz-box-sizing : border-box;
color: white;
background: rgba(0,0,0,0.5);
display : none;
}
@ -23,7 +24,6 @@
box-sizing: border-box;
-moz-box-sizing : border-box;
padding: 20px 3%;
border-radius: 3px;
background: rgba(0,0,0,0.9);
overflow: auto;

View File

@ -0,0 +1,203 @@
.palette-manager-wrapper {
height: 100%;
position: relative;
}
.palette-manager-body {
position: absolute;
top: 45px;
bottom: 0;
right: 0;
left: 0;
}
.palette-manager-head {
position: absolute;
width: 100%;
background: gold;
margin: 0;
padding: 10px;
color: black;
font-size: 1.8em;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.palette-manager-drawer {
width: 200px;
position: absolute;
top: 0;
bottom: 0;
}
.palette-manager-list {
position: absolute;
top:40px;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
}
.palette-manager-actions {
position: absolute;
height:40px;
line-height:40px;
width: 100%;
text-align: center;
}
.palette-manager-actions-button {
width: 80px;
margin: 5px;
}
.palette-manager-list li {
padding-left:10px;
font-size: 1.4em;
height: 48px;
line-height: 48px;
box-sizing: border-box;
-moz-box-sizing: border-box;
border-bottom: 1px solid #666;
cursor:pointer;
}
.palette-manager-list li:hover {
background : #222;
}
.palette-manager-list li.selected {
color : gold;
font-weight: bold;
}
.palette-manager-list li:nth-child(1) {
border-top: 1px solid #666;
}
.palette-manager-details {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 200px;
box-sizing: border-box;
-moz-box-sizing: border-box;
border-left:1px solid #666;
}
.palette-manager-details-head {
position: absolute;
height:40px;
line-height:40px;
width: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.palette-manager-details-head-name {
padding: 0 10px 0 20px;
font-size: 1.5em;
font-weight: bold;
}
.palette-manager-details-head-edit-icon {
cursor: pointer;
width: 24px;
height: 100%;
background: url('../../img/tools/pen.png');
display: inline-block;
background-size: 20px;
background-repeat: no-repeat;
background-position-y: 50%;
}
.palette-manager-details-head-actions {
float: right;
line-height: 40px;
padding-right: 10px;
}
.palette-manager-details-body {
position: absolute;
top:40px;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.palette-manager-color-card {
width: 120px;
height: 180px;
display: inline-block;
position: relative;
margin: 20px 0 20px 20px;
}
.palette-manager-delete-card {
position: absolute;
top: 0;
right: 0;
width: 20px;
text-align: center;
font-size: 1.6em;
font-weight: bold;
color: rgb(255,255,255);
text-shadow : 0 0 2px rgb(0,0,0);
cursor: pointer;
opacity : 0.2;
transition : opacity 0.3s, color 0.1s;
}
.palette-manager-color-card:hover .palette-manager-delete-card {
opacity : 0.6;
}
.palette-manager-color-card .palette-manager-delete-card:hover {
opacity : 1;
color: rgb(240,80,80);
}
.palette-manager-new-color .palette-manager-color-square {
border: 3px dotted #888;
border-bottom-width: 0;
box-sizing: border-box;
-moz-box-sizing: border-box;
border-radius: 3px 3px 0 0;
cursor: pointer;
text-align: center;
font-size: 24px;
color: #888;
line-height: 120px;
}
.palette-manager-color-square {
width: 120px;
height: 120px;
cursor: pointer;
/*background-image:url(../../img/tools/eyedropper.png);*/
}
.palette-manager-color-details {
color : #888;
background: #eee;
height: 60px;
padding-left: 5px;
}
.palette-manager-color-details li{
line-height: 20px;
}

View File

@ -64,8 +64,8 @@
color: #333;
}
.button[disabled=disabled],
.button[disabled=disabled]:hover {
.button[disabled],
.button[disabled]:hover {
cursor:default;
background-color: #aaa;
color: #777;

View File

@ -7,7 +7,7 @@
border-radius: 4px;
background-color: #2B2B2B;
border: solid 4px #888;
padding: 5px;
padding: 5px 5px 0 5px;
box-shadow : 0 0 5px 0 black;
}
@ -71,6 +71,12 @@
border-right-width: 0;
}
.sp-picker-container {
padding: 5px;
padding-bottom: 300px;
margin-bottom: -295px;
}
.sp-slider {
height: 5px;
left: -2px;
@ -96,4 +102,17 @@
.sp-palette .sp-thumb-el.sp-thumb-active {
border-color: gold;
box-shadow: 0 0 0px 1px gold;
}
.sp-input {
border: 1px solid #666;
margin: 0;
background: #111;
border-radius: 2px;
color: #D3D3D3;
font-family: Courier!important;
}
.sp-input.sp-validation-error {
background: #330000;
}

View File

@ -11,6 +11,15 @@ body {
display: none;
}
.allow-user-select {
-webkit-touch-callout: initial;
-webkit-user-select: initial;
-khtml-user-select: initial;
-moz-user-select: initial;
-ms-user-select: initial;
user-select: initial;
}
/**
* Application layout
*/

View File

@ -18,6 +18,9 @@ var Constants = {
DEFAULT_PEN_COLOR : '#000000',
TRANSPARENT_COLOR : 'rgba(0, 0, 0, 0)',
// Used for Spectrum input
PREFERRED_COLOR_FORMAT : 'rgb',
/*
* Fake semi-transparent color used to highlight transparent
* strokes and rectangles:

View File

@ -22,6 +22,7 @@
var spectrumCfg = {
showPalette: true,
showButtons: false,
showInput: true,
palette: [
['rgba(0,0,0,0)']
],

View File

@ -4,12 +4,13 @@
var dialogs = {
'manage-palettes' : {
template : 'templates/dialogs/manage-palettes.html',
controller : ns.ManagePalettesController
controller : ns.PaletteManagerController
}
};
ns.DialogsController = function (piskelController) {
this.piskelController = piskelController;
this.currentDialog_ = null;
};
ns.DialogsController.prototype.init = function () {
@ -17,17 +18,20 @@
this.dialogWrapper_ = document.getElementById('dialog-container-wrapper');
$.subscribe(Events.DIALOG_DISPLAY, this.onDialogDisplayEvent_.bind(this));
$.subscribe(Events.DIALOG_HIDE, this.onDialogHideEvent_.bind(this));
pskl.app.shortcutService.addShortcut('M', this.onDialogDisplayEvent_.bind(this, null, 'manage-palettes'));
};
ns.DialogsController.prototype.onDialogDisplayEvent_ = function (evt, dialogId) {
var config = dialogs[dialogId];
if (config) {
this.dialogContainer_.innerHTML = pskl.utils.Template.get(config.template);
(new config.controller(this.piskelController)).init();
this.showDialogWrapper_();
} else {
console.error('Could not find dialog configuration for dialogId : ' + dialogId);
if (!this.isDisplayed()) {
var config = dialogs[dialogId];
if (config) {
this.dialogContainer_.innerHTML = pskl.utils.Template.get(config.template);
(new config.controller(this.piskelController)).init();
this.showDialogWrapper_();
this.currentDialog_ = dialogId;
} else {
console.error('Could not find dialog configuration for dialogId : ' + dialogId);
}
}
};
@ -36,13 +40,22 @@
};
ns.DialogsController.prototype.showDialogWrapper_ = function () {
pskl.app.shortcutService.addShortcut('ESC', this.hideDialogWrapper_.bind(this));
pskl.app.shortcutService.addShortcut('ESC', this.hideDialog.bind(this));
this.dialogWrapper_.style.display = 'block';
};
ns.DialogsController.prototype.hideDialog = function () {
this.hideDialogWrapper_();
this.currentDialog_ = null;
};
ns.DialogsController.prototype.hideDialogWrapper_ = function () {
pskl.app.shortcutService.removeShortcut('ESC');
this.dialogWrapper_.style.display = 'none';
};
ns.DialogsController.prototype.isDisplayed = function () {
return this.currentDialog_ !== null;
};
})();

View File

@ -1,9 +0,0 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
ns.ManagePalettesController = function (piskelController) {
this.piskelController = piskelController;
};
ns.ManagePalettesController.prototype.init = function () {};
})();

View File

@ -0,0 +1,326 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
var tinycolor = window.tinycolor;
var SELECTED_CLASSNAME = 'selected';
var NEW_COLOR_CLASS = 'palette-manager-new-color';
var CLOSE_ICON_CLASS = 'palette-manager-delete-card';
var EDIT_NAME_CLASS = 'palette-manager-details-head-edit-icon';
ns.PaletteManagerController = function (piskelController) {
this.piskelController = piskelController;
this.palettes = this.retrieveUserPalettes();
this.originalPalettes = this.retrieveUserPalettes();
this.selectedPaletteId = null;
};
ns.PaletteManagerController.prototype.init = function () {
this.palettesList = document.querySelector('.palette-manager-list');
this.paletteBody = document.querySelector('.palette-manager-details-body');
this.paletteHead = document.querySelector('.palette-manager-details-head');
this.createButton = document.querySelector('.palette-manager-actions-button[data-action="create"]');
this.colorCardTemplate = pskl.utils.Template.get('palette-color-card-template');
this.newColorTemplate = pskl.utils.Template.get('palette-new-color-template');
this.paletteHeadTemplate = pskl.utils.Template.get('palette-details-head-template');
// Events
this.palettesList.addEventListener('click', this.onPaletteListClick.bind(this));
// Delegated event listener for events repeated on all cards
this.paletteBody.addEventListener('click', this.delegatedPaletteBodyClick.bind(this));
this.paletteHead.addEventListener('click', this.delegatedPaletteHeadClick.bind(this));
this.createButton.addEventListener('click', this.createPalette.bind(this));
// Init markup
this.createPaletteListMarkup();
if (this.palettes.length > 0) {
this.selectPalette(this.palettes[0].id);
} else {
console.error('[PaletteManagerController] >>> Implement fallback screen when no palette can be retrieved');
}
};
ns.PaletteManagerController.prototype.createPalette = function () {
var name = window.prompt('Please enter a name for your palette', 'New palette');
if (name) {
var palette = this.createPaletteObject(name);
this.palettes.push(palette);
this.createPaletteListMarkup();
this.selectPalette(palette.id);
}
};
ns.PaletteManagerController.prototype.createPaletteObject = function (name) {
return {
id : 'palette-' + Date.now() + '-' + Math.floor(Math.random()*1000),
name : name,
colors : []
};
};
ns.PaletteManagerController.prototype.selectPalette = function (paletteId) {
this.deselectCurrentPalette();
var paletteListItem = this.palettesList.querySelector('[data-palette-id='+paletteId+']');
if (paletteListItem) {
this.selectedPaletteId = paletteId;
paletteListItem.classList.add(SELECTED_CLASSNAME);
this.refreshPaletteDetails();
}
};
ns.PaletteManagerController.prototype.refreshPaletteDetails = function () {
this.createPaletteHeadMarkup();
this.createPaletteBodyMarkup();
this.initPaletteDetailsEvents();
this.initPaletteCardsSpectrum();
};
ns.PaletteManagerController.prototype.createPaletteListMarkup = function () {
var html = this.palettes.map(function (palette) {
var paletteCopy = {
id : palette.id,
name : this.isPaletteModified(palette) ? palette.name + " *" : palette.name
};
return pskl.utils.Template.replace('<li data-palette-id="{{id}}">{{name}}</li>', paletteCopy);
}.bind(this)).join('');
this.palettesList.innerHTML = html;
};
/**
* Fill the palette body container with color cards for the selected palette
*/
ns.PaletteManagerController.prototype.createPaletteHeadMarkup = function () {
var palette = this.getSelectedPalette();
var dict = {
'name' : palette.name,
'save:disabled' : !this.isPaletteModified(palette),
'revert:disabled' : !this.isPaletteModified(palette),
'delete:disabled' : this.palettes.length < 2
};
var html = pskl.utils.Template.replace(this.paletteHeadTemplate, dict);
this.paletteHead.innerHTML = html;
};
ns.PaletteManagerController.prototype.isPaletteModified = function (palette) {
var isModified = false;
var originalPalette = this.getPaletteById(palette.id, this.originalPalettes);
if (originalPalette) {
var differentName = originalPalette.name !== palette.name;
var differentColors = palette.colors.join('') !== originalPalette.colors.join('');
isModified = differentName || differentColors;
} else {
isModified = true;
}
return isModified;
};
/**
* Fill the palette body container with color cards for the selected palette
*/
ns.PaletteManagerController.prototype.createPaletteBodyMarkup = function () {
var palette = this.getSelectedPalette();
var html = this.getColorCardsMarkup(palette.colors);
html += pskl.utils.Template.replace(this.newColorTemplate, {classname : NEW_COLOR_CLASS});
this.paletteBody.innerHTML = html;
};
ns.PaletteManagerController.prototype.initPaletteDetailsEvents = function () {
// New Card click event
var newCard = this.paletteBody.querySelector('.' + NEW_COLOR_CLASS);
newCard.addEventListener('click', this.addColorInSelectedPalette.bind(this, '#FFFFFF'));
if (this.palettes.length < 2) {
var deleteButton = this.paletteHead.querySelector('.palette-manager-palette-button[data-action="delete"]');
deleteButton.setAttribute("disabled", "disabled");
}
};
ns.PaletteManagerController.prototype.delegatedPaletteBodyClick = function (event) {
var target = event.target;
if (target.classList.contains(CLOSE_ICON_CLASS)) {
var colorId = parseInt(target.parentNode.dataset.colorId, 10);
this.removeColorInSelectedPalette(colorId);
}
};
ns.PaletteManagerController.prototype.delegatedPaletteHeadClick = function (event) {
var target = event.target;
if (target.classList.contains(EDIT_NAME_CLASS)) {
this.renameSelectedPalette();
} else if (target.classList.contains('palette-manager-palette-button')) {
var action = target.dataset.action;
if (action === 'save') {
this.savePalette(this.getSelectedPalette().id);
} else if (action === 'revert') {
this.revertChanges();
} else if (action === 'delete') {
this.deleteSelectedPalette();
}
}
};
ns.PaletteManagerController.prototype.initPaletteCardsSpectrum = function () {
var oSelf = this;
var colorSquares = $(':not(.' + NEW_COLOR_CLASS + ')>.palette-manager-color-square');
colorSquares.spectrum({
clickoutFiresChange : true,
showInput: true,
showButtons: false,
change : function (color) {
var target = this;
var colorId = parseInt(target.parentNode.dataset.colorId, 10);
oSelf.updateColorInSelectedPalette(colorId, color);
},
beforeShow : function() {
var target = this;
var colorId = parseInt(target.parentNode.dataset.colorId, 10);
var palette = oSelf.getSelectedPalette();
var color = palette.colors[colorId];
colorSquares.spectrum("set", color);
}
});
};
ns.PaletteManagerController.prototype.updateColorInSelectedPalette = function (colorId, color) {
var palette = this.getSelectedPalette();
palette.colors.splice(colorId, 1, '#' + (color.toHex().toUpperCase()));
this.createPaletteListMarkup();
this.selectPalette(this.selectedPaletteId);
};
ns.PaletteManagerController.prototype.addColorInSelectedPalette = function (color) {
var selectedPalette = this.getSelectedPalette();
selectedPalette.colors.push(color);
this.createPaletteListMarkup();
this.selectPalette(this.selectedPaletteId);
};
ns.PaletteManagerController.prototype.removeColorInSelectedPalette = function (colorId) {
var palette = this.getSelectedPalette();
palette.colors.splice(colorId, 1);
this.createPaletteListMarkup();
this.selectPalette(this.selectedPaletteId);
};
ns.PaletteManagerController.prototype.renameSelectedPalette = function () {
var selectedPalette = this.getSelectedPalette();
var name = window.prompt('Please enter a new name for palette "' + selectedPalette.name + '"');
if (name) {
selectedPalette.name = name;
this.createPaletteListMarkup();
this.selectPalette(selectedPalette.id);
}
};
ns.PaletteManagerController.prototype.getSelectedPalette = function () {
return this.getPaletteById(this.selectedPaletteId, this.palettes);
};
ns.PaletteManagerController.prototype.getColorCardsMarkup = function (colors) {
var html = colors.map(function (color, index) {
var dict = {
colorId : index,
hex : color,
rgb : tinycolor(color).toRgbString(),
hsl : tinycolor(color).toHslString()
};
return pskl.utils.Template.replace(this.colorCardTemplate, dict);
}.bind(this)).join('');
return html;
};
ns.PaletteManagerController.prototype.getPaletteById = function (paletteId, palettes) {
var match = null;
palettes.forEach(function (palette) {
if (palette.id === paletteId) {
match = palette;
}
});
return match;
};
ns.PaletteManagerController.prototype.removePaletteById = function (paletteId, palettes) {
var palette = this.getPaletteById(paletteId, palettes);
if (palette) {
var index = palettes.indexOf(palette);
palettes.splice(index, 1);
}
};
ns.PaletteManagerController.prototype.deselectCurrentPalette = function () {
var selectedItem = this.palettesList.querySelector('.' + SELECTED_CLASSNAME);
if (selectedItem) {
this.selectedPaletteId = null;
selectedItem.classList.remove(SELECTED_CLASSNAME);
}
};
ns.PaletteManagerController.prototype.revertChanges = function () {
var palette = this.getSelectedPalette();
var originalPalette = this.getPaletteById(palette.id, this.originalPalettes);
palette.name = originalPalette.name;
palette.colors = originalPalette.colors.slice(0);
this.createPaletteListMarkup();
this.selectPalette(palette.id);
};
ns.PaletteManagerController.prototype.deleteSelectedPalette = function () {
var palette = this.getSelectedPalette();
if (this.palettes.length > 1) {
if (window.confirm('Are you sure you want to delete "' + palette.name + '" ?')) {
this.removePaletteById(palette.id, this.palettes);
this.removePaletteById(palette.id, this.originalPalettes);
this.persistToLocalStorage();
this.createPaletteListMarkup();
this.selectPalette(this.palettes[0].id);
}
}
};
ns.PaletteManagerController.prototype.onPaletteListClick = function (event) {
var target = event.target;
if (target.dataset.paletteId) {
this.selectPalette(target.dataset.paletteId);
}
};
ns.PaletteManagerController.prototype.savePalette = function (paletteId) {
var palette = this.getPaletteById(paletteId, this.palettes);
var originalPalette = this.getPaletteById(paletteId, this.originalPalettes);
if (originalPalette) {
originalPalette.name = palette.name;
originalPalette.colors = palette.colors;
} else {
this.originalPalettes.push(palette);
}
this.persistToLocalStorage();
this.createPaletteListMarkup();
this.selectPalette(this.getSelectedPalette().id);
};
ns.PaletteManagerController.prototype.persistToLocalStorage = function () {
window.localStorage.setItem('piskel.palettes', JSON.stringify(this.originalPalettes));
this.originalPalettes = this.retrieveUserPalettes();
};
ns.PaletteManagerController.prototype.retrieveUserPalettes = function () {
var palettesString = window.localStorage.getItem('piskel.palettes');
return JSON.parse(palettesString) || [this.createPaletteObject('New palette')];
};
})();

View File

@ -670,7 +670,7 @@
// Update the text entry input as it changes happen
if (opts.showInput) {
textInput.val(realColor.toString(format));
textInput.val(realColor.toString(Constants.PREFERRED_COLOR_FORMAT || format));
}
if (opts.showPalette) {

View File

@ -21,7 +21,8 @@
this.shortcuts_[key] = this.shortcuts_[key] || {};
if (this.shortcuts_[key][meta]) {
throw 'Shortcut ' + meta + ' + ' + key + ' already registered';
var keyStr = (meta !== 'normal' ? meta + ' + ' : '') + key;
console.error('[ShortcutService] >>> Shortcut [' + keyStr + '] already registered');
} else {
this.shortcuts_[key][meta] = callback;
}

View File

@ -21,6 +21,16 @@
for (var key in dict) {
if (dict.hasOwnProperty(key)) {
var value = dict[key];
// special boolean keys keys key:default
// if the value is a boolean, use default as value
if (key.indexOf(':') !== -1) {
if (value === true) {
value = key.split(':')[1];
} else if (value === false) {
value = '';
}
}
template = template.replace(new RegExp('\\{\\{'+key+'\\}\\}', 'g'), value);
}
}

View File

@ -82,7 +82,7 @@ exports.scripts = [
"js/controller/settings/SettingsController.js",
// Dialogs sub-controllers
"js/controller/dialogs/ManagePalettesController.js",
"js/controller/dialogs/PaletteManagerController.js",
// Dialogs controller
"js/controller/dialogs/DialogsController.js",

View File

@ -8,6 +8,7 @@ exports.styles = [
"css/tools.css",
"css/cheatsheet.css",
"css/dialogs/dialogs.css",
"css/dialogs/manage-palettes.css",
"css/toolbox/toolbox.css",
"css/toolbox/layers-list.css",
"css/toolbox/palettes-list.css",

View File

@ -1,3 +1,56 @@
<div>
BITE BITE BITE
</div>
<div class="palette-manager-wrapper">
<h3 class="palette-manager-head">
Palette manager
</h3>
<div class="palette-manager-body">
<div class="palette-manager-drawer">
<div class="palette-manager-actions">
<button type="button" class="palette-manager-actions-button button " data-action="create">Create</button>
<button type="button" class="palette-manager-actions-button button " data-action="save-all">Save all</button>
</div>
<ul class="palette-manager-list">
</ul>
</div>
<div class="palette-manager-details">
<div class="palette-manager-details-head"></div>
<div class="palette-manager-details-body"></div>
</div>
</div>
</div>
<script type="text/template" id="palette-details-head-template">
<span class="palette-manager-details-head-name">{{name}}</span>
<span class="palette-manager-details-head-edit-icon">&nbsp;</span>
<div class="palette-manager-details-head-actions">
<button class="palette-manager-palette-button button button-primary" {{save:disabled}} data-action="save" type="button">Save</button>
<button class="palette-manager-palette-button button " {{revert:disabled}} data-action="revert" type="button">Revert</button>
<button class="palette-manager-palette-button button " {{delete:disabled}} data-action="delete" type="button">Delete</button>
</div>
</script>
<script type="text/template" id="palette-color-card-template">
<div class="palette-manager-color-card" data-color-id="{{colorId}}">
<span class="palette-manager-delete-card" title="remove this color">X</span>
<div class="palette-manager-color-square" style="background-color:{{hex}}"></div>
<div class="palette-manager-color-details allow-user-select">
<ul>
<li>{{hex}}</li>
<li>{{rgb}}</li>
<li>{{hsl}}</li>
</ul>
</div>
</div>
</script>
<script type="text/template" id="palette-new-color-template">
<div class="palette-manager-color-card {{classname}}">
<div class="palette-manager-color-square">Add</div>
<div class="palette-manager-color-details">
<ul>
<li>Hex</li>
<li>RGB</li>
<li>HSL</li>
</ul>
</div>
</div>
</script>