@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,179 lines (1,006 loc) • 41.1 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.m.ColorPalette
sap.ui.define([
'sap/ui/core/Control',
'sap/ui/Device',
'sap/ui/base/DataType',
'sap/ui/core/library',
'sap/ui/core/delegate/ItemNavigation',
'./Button',
'./Dialog',
'./library',
'./ColorPaletteRenderer',
"sap/ui/dom/containsOrEquals",
"sap/ui/events/KeyCodes",
"sap/ui/thirdparty/jquery",
"sap/ui/unified/library",
"sap/ui/unified/ColorPickerDisplayMode",
"sap/ui/unified/ColorPicker"
], function(
Control,
Device,
DataType,
coreLibrary,
ItemNavigation,
Button,
Dialog,
library,
ColorPaletteRenderer,
containsOrEquals,
KeyCodes,
jQuery,
unifiedLibrary,
ColorPickerDisplayMode,
ColorPicker
) {
"use strict";
// shortcut to CSSColor of the core library
var CSSColor = coreLibrary.CSSColor;
// shortcut to ColorPickerMode
var ColorPickerMode = unifiedLibrary.ColorPickerMode;
// shortcut to the ButtonType enumeration
var ButtonType = library.ButtonType;
// reference to the boolean data type in the core
var BooleanType = DataType.getType("boolean");
// The name of the class, corresponding to a single color item
var CSS_CLASS_SWATCH = "sapMColorPaletteSquare";
// Defines the exact count of swatches per row
var SWATCHES_PER_ROW = 5;
// Min amount of swatches when app.developer sets colors
var MIN_COLORS = 2;
// Max amount of swatches when app.developer sets colors
var MAX_COLORS = 15;
// get resource translation bundle;
var oLibraryResourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.m");
/**
* Constructor for a new <code>ColorPalette</code>.
*
* @param {string} [sId] ID for the new control, generated automatically if no ID is given
* @param {object} [mSettings] Initial settings for the new control
*
* @class
* Represents a predefined range of colors for easier selection.
*
* <h3>Overview</h3>
* The <code>ColorPalette</code> provides the users with a range of predefined colors.
*
* You can customize them with the use of the <code>colors</code> property.
* You can specify a <code>defaultColor</code> and display a "Default color" button
* for the user to choose directly. You can display a "More colors..." button
* that opens an additional color picker for the user to choose specific colors
* that are not present in the predefined range.
*
* <h3>Usage</h3>
*
* The palette is intended for users, who don't want to check and remember the
* different values of the colors and spend large amount of time to configure the
* right color through the color picker.
*
* The control can be embedded in a form or can be opened as popover (by use of thin
* wrapper control <code>sap.m.ColorPalettePopover</code>).
* @see {@link sap.m.ColorPalettePopover}
*
* <b>Note:</b> The application developers should add dependency to <code>sap.ui.unified</code> library
* on application level to ensure that the library is loaded before the module dependencies will be required.
* The {@link sap.ui.unified.ColorPicker} is used internally only if the <code>ColorPicker</code>
* is opened (not used for the initial rendering). If the <code>sap.ui.unified</code> library is not loaded
* before the <code>ColorPicker</code> is opened, it will be loaded upon opening. This could lead to CSP compliance
* issues and adds an additional waiting time when the <code>ColorPicker</code> is opened for the first time.
* To prevent this, apps using the <code>ColorPalette</code> should also load the <code>sap.ui.unified</code> library in advance.
*
* @extends sap.ui.core.Control
* @version 1.117.4
*
* @constructor
* @public
* @since 1.54
* @alias sap.m.ColorPalette
*/
var ColorPalette = Control.extend("sap.m.ColorPalette", /** @lends sap.m.ColorPalette.prototype */ {
metadata: {
library: "sap.m",
properties: {
/**
* Defines the List of colors displayed in the palette. Minimum is 2 colors, maximum is 15 colors.
*/
colors: {
type: "sap.ui.core.CSSColor[]", group: "Appearance",
defaultValue: [
"gold",
"darkorange",
"indianred",
"darkmagenta",
"cornflowerblue",
"deepskyblue",
"darkcyan",
"olivedrab",
"darkslategray",
"azure",
"white",
"lightgray",
"darkgray",
"dimgray",
"black"
]
}
},
aggregations: {
/* Reference to an optional button for activating default color. For private use. */
_defaultColorButton: {type: "sap.m.Button", multiple: false, visibility: "hidden"},
/* Reference to an optional button for activating more color selection. For private use. */
_moreColorsButton: {type: "sap.m.Button", multiple: false, visibility: "hidden"}
},
events: {
/**
* Fired when the user selects a color.
*/
colorSelect: {
parameters: {
/**
* The color that is returned when user chooses the "Default color" button.
*/
"value": {type: "sap.ui.core.CSSColor"},
/**
* Denotes if the color has been chosen by selecting the "Default Color" button (true or false)
*/
"defaultAction": {type: "boolean"}
}
},
/**
* Fired when the value is changed by user interaction in the internal ColorPicker
*
* @since 1.85
*/
liveChange: {
parameters : {
/**
* Parameter containing the RED value (0-255).
*/
r : {type: "int"},
/**
* Parameter containing the GREEN value (0-255).
*/
g : {type: "int"},
/**
* Parameter containing the BLUE value (0-255).
*/
b : {type: "int"},
/**
* Parameter containing the HUE value (0-360).
*/
h : {type: "int"},
/**
* Parameter containing the SATURATION value (0-100).
*/
s : {type: "int"},
/**
* Parameter containing the VALUE value (0-100).
*/
v : {type: "int"},
/**
* Parameter containing the LIGHTNESS value (0-100).
*/
l : {type: "int"},
/**
* Parameter containing the Hexadecimal string (#FFFFFF).
*/
hex : {type: "string"},
/**
* Parameter containing the alpha value (transparency).
*/
alpha : {type: "string"}
}
}
}
},
renderer: ColorPaletteRenderer
});
ColorPalette.prototype.init = function () {
// The default color. Private API. Allowed consumer is ColorPaletteAPI.
this._oDefaultColor = null;
// If the "Default color" button should be shown. Private API. Allowed consumer is ColorPaletteAPI.
this._bShowDefaultColorButton = false;
// If the "More colors" button should be shown. Private API. Allowed consumer is ColorPaletteAPI.
this._bShowMoreColorsButton = false;
// If the "Recent colors" section should be shown. Private API. Allowed consumer is ColorPaletteAPI
this._bShowRecentColorsSection = false;
// Display mode. Private API. Allowed consumer is ColorPaletteAPI.
this._oDisplayMode = ColorPickerDisplayMode.Default;
// Reference to the dialog containing the internal "Color Picker". For private use.
this._oMoreColorsDialog = null;
// Reference to the palette color section item navigation
this._oPaletteColorItemNavigation = null;
// Reference to the recent color section item navigation
this._oRecentColorItemNavigation = null;
// Queue of recently used colors
this._recentColors = [];
};
ColorPalette.prototype.exit = function () {
if (this._oMoreColorsDialog) {
this._oMoreColorsDialog.destroy();
delete this._oMoreColorsDialog;
}
if (this._oPaletteColorItemNavigation) {
this.removeDelegate(this._oPaletteColorItemNavigation);
this._oPaletteColorItemNavigation.destroy();
delete this._oPaletteColorItemNavigation;
}
if (this._oRecentColorItemNavigation) {
this.removeDelegate(this._oRecentColorItemNavigation);
this._oRecentColorItemNavigation.destroy();
delete this._oRecentColorItemNavigation;
}
};
ColorPalette.prototype.setColors = function (aColors) {
aColors = this.validateProperty("colors", aColors);
if (aColors.length < MIN_COLORS || aColors.length > MAX_COLORS) {
throw new Error("Cannot set property 'colors' - array must has minimum 2 and maximum 15 elements");
}
return this.setProperty("colors", aColors);
};
/**
* Sets a default displayMode.
* @param {sap.ui.unified.ColorPickerDisplayMode} oDisplayMode the color
* @private
* @return {this} <code>this</code> for method chaining
*/
ColorPalette.prototype._setDisplayMode = function (oDisplayMode) {
var oColorPicker = this._getColorPicker();
oColorPicker.setDisplayMode(oDisplayMode);
this._oDisplayMode = oDisplayMode;
return this;
};
// Display mode
ColorPalette.prototype._getDisplayMode = function () {
return this._oDisplayMode;
};
ColorPalette.prototype._getColorPicker = function () {
return this._ensureMoreColorsDialog()._oColorPicker;
};
ColorPalette.prototype.ontap = function (oEvent) {
var $Target = jQuery(oEvent.target),
sColor,
$Swatch;
$Swatch = $Target.closest("." + CSS_CLASS_SWATCH);
if (!$Swatch.length) {
return;
}
sColor = $Swatch.attr("data-sap-ui-color");
this._fireColorSelect(sColor, false, oEvent);
};
ColorPalette.prototype.onsaptabnext = ColorPalette.prototype.onsaptabprevious = function (oEvent) {
var oElementInfo = this._getElementInfo(oEvent.target);
if (oElementInfo.bIsMoreColorsButton) {
this.fireEvent("_colorNotSelected", {_originalEvent: oEvent});
return;
}
if (oElementInfo.bIsDefaultColorButton) {
this._fireColorSelect(this._getDefaultColor(), true, oEvent);
return;
}
ColorPalette.prototype.ontap.apply(this, arguments);
};
ColorPalette.prototype.onsapenter = ColorPalette.prototype.ontap;
ColorPalette.prototype.onsapspace = function (oEvent) {
oEvent.preventDefault();
};
ColorPalette.prototype.onkeyup = function (oEvent) {
if (oEvent.which === KeyCodes.SPACE) {
oEvent.preventDefault();
ColorPalette.prototype.ontap.apply(this, arguments);
}
};
ColorPalette.prototype.onAfterRendering = function () {
this._ensureItemNavigation();
};
ColorPalette.prototype.pushToRecentColors = function (sColor) {
var iIndexOfColor = this._recentColors.indexOf(sColor);
if (iIndexOfColor > -1){
this._recentColors.splice(iIndexOfColor,1);
} else if (this._recentColors.length === 5) {
this._recentColors.pop();
}
this._recentColors.unshift(sColor);
this.invalidate();
};
/**
* Sets a selected color for the ColorPicker control.
* @param {sap.ui.core.CSSColor} color the selected color
* @public
* @return {this} <code>this</code> for method chaining
*/
ColorPalette.prototype.setColorPickerSelectedColor = function (color) {
if (!CSSColor.isValid(color)) {
throw new Error("Cannot set the selected color - invalid value: " + color);
}
this._getColorPicker().setColorString(color);
return this;
};
// Private methods -------------------------------------------------------------------------------------------
ColorPalette.prototype._createDefaultColorButton = function () {
return new Button(this.getId() + "-btnDefaultColor", {
width: "100%",
type: ButtonType.Transparent,
text: oLibraryResourceBundle.getText("COLOR_PALETTE_DEFAULT_COLOR"),
visible: this._getShowDefaultColorButton(),
press: function (oEvent) {
this._fireColorSelect(this._getDefaultColor(), true, oEvent);
}.bind(this)
});
};
// Default color
ColorPalette.prototype._getDefaultColor = function () {
return this._oDefaultColor;
};
/**
* Sets a default color.
* @param {sap.ui.core.CSSColor} color the color
* @private
* @return {this} <code>this</code> for method chaining
*/
ColorPalette.prototype._setDefaultColor = function (color) {
if (!CSSColor.isValid(color)) {
throw new Error("Cannot set internal property '_defaultColor' - invalid value: " + color);
}
this._oDefaultColor = color;
return this;
};
ColorPalette.prototype._getShowDefaultColorButton = function () {
return this._bShowDefaultColorButton;
};
ColorPalette.prototype._setShowDefaultColorButton = function (bValue) {
if (!BooleanType.isValid(bValue)) {
throw new Error("Cannot set internal property 'showDefaultColorButton' - invalid value: " + bValue);
}
this._bShowDefaultColorButton = bValue;
//lazy loading check
if (bValue && !this._getDefaultColorButton()) {
this.setAggregation("_defaultColorButton", this._createDefaultColorButton());
}
if (this._getDefaultColorButton()) { /* still button may not be there */
this._getDefaultColorButton().setVisible(bValue);
}
return this;
};
ColorPalette.prototype._getDefaultColorButton = function () {
return this.getAggregation("_defaultColorButton");
};
// More Colors...
ColorPalette.prototype._createMoreColorsButton = function () {
return new Button(this.getId() + "-btnMoreColors", {
width: "100%",
type: ButtonType.Transparent,
text: oLibraryResourceBundle.getText("COLOR_PALETTE_MORE_COLORS"),
visible: this._getShowMoreColorsButton(),
press: this._openColorPicker.bind(this)
});
};
ColorPalette.prototype._getShowMoreColorsButton = function () {
return this._bShowMoreColorsButton;
};
ColorPalette.prototype._getShowRecentColorsSection = function () {
return this._bShowRecentColorsSection;
};
ColorPalette.prototype._getRecentColors = function () {
return this._recentColors;
};
ColorPalette.prototype._setShowRecentColorsSection = function (bValue) {
if (!BooleanType.isValid(bValue)) {
throw new Error("Cannot set internal property 'showRecentColorsSection' - invalid value: " + bValue);
}
this._bShowRecentColorsSection = bValue;
return this;
};
ColorPalette.prototype._setShowMoreColorsButton = function (bValue) {
if (!BooleanType.isValid(bValue)) {
throw new Error("Cannot set internal property 'showMoreColorsButton' - invalid value: " + bValue);
}
this._bShowMoreColorsButton = bValue;
//lazy loading check
if (bValue && !this._getMoreColorsButton()) {
this.setAggregation("_moreColorsButton", this._createMoreColorsButton());
}
if (this._getMoreColorsButton()) { /* still button may not be there */
this._getMoreColorsButton().setVisible(bValue);
}
return this;
};
ColorPalette.prototype._getMoreColorsButton = function () {
return this.getAggregation("_moreColorsButton");
};
/**
* Opens a color picker in a Dialog.
* The function assumes that there is a "more colors.." button visible.
* @return void
* @private
*/
ColorPalette.prototype._openColorPicker = function () {
this.fireEvent("_beforeOpenColorPicker"); //hook for program consumers (i.e. ColorPalettePopover)
this._ensureMoreColorsDialog().open();
};
/**
* Ensure a dialog wrapping the ColorPicker exists (creates new if is not created yet).
* @return {sap.m.Dialog} the dialog.
* @private
*/
ColorPalette.prototype._ensureMoreColorsDialog = function () {
if (!this._oMoreColorsDialog) {
this._oMoreColorsDialog = this._createMoreColorsDialog();
}
return this._oMoreColorsDialog;
};
/**
* Creates a dialog that wraps the ColorPicker
* @return {sap.m.Dialog} the dialog containing the ColorPicker
* @private
*/
ColorPalette.prototype._createMoreColorsDialog = function () {
var oDialog = new Dialog(this.getId() + "-moreColorsDialog", {
stretch: !!Device.system.phone,
title: oLibraryResourceBundle.getText("COLOR_PALETTE_MORE_COLORS_TITLE")
}).addStyleClass("CPDialog");
// keep explicit reference to the picker attached to the parent dialog
oDialog.addContent(oDialog._oColorPicker = new ColorPicker({
mode: ColorPickerMode.HSL,
displayMode: this._oDisplayMode,
liveChange: function (oEvent) {
this.fireLiveChange(oEvent.getParameters());
}.bind(this)
}));
// OK button
oDialog.setBeginButton(new Button({
text: oLibraryResourceBundle.getText("COLOR_PALETTE_MORE_COLORS_CONFIRM"),
press: function (oEvent) {
oDialog.close();
if (oDialog._oColorPicker.getColorString()) {
this._fireColorSelect(oDialog._oColorPicker.getColorString(), false, oEvent);
}
}.bind(this)
}));
// Cancel button
oDialog.setEndButton(new Button({
text: oLibraryResourceBundle.getText("COLOR_PALETTE_MORE_COLORS_CANCEL"),
press: function () {
oDialog.close();
}
}));
return oDialog;
};
// Other
/**
* Focuses the first available element in the palette.
* @private
*/
ColorPalette.prototype._focusFirstElement = function () {
var oFirstSwatchElement = this._getShowDefaultColorButton() ? this._getDefaultColorButton().getDomRef() : this._getAllPaletteColorSwatches()[0];
oFirstSwatchElement.focus();
};
/**
* Helper function to fire the event "colorSelect"
* @param {sap.ui.core.CSSColor} color the color
* @param {boolean} [defaultAction=false] if the selection is performed via "Default color" button
* @param {jQuery.Event} oOriginalEvent original event
* @private
*/
ColorPalette.prototype._fireColorSelect = function (color, defaultAction, oOriginalEvent) {
this.fireColorSelect({value: color, defaultAction: defaultAction, _originalEvent: oOriginalEvent});
this.pushToRecentColors(color);
};
/**
* Handles creation or update of the ItemNavigation.
* @private
*/
ColorPalette.prototype._ensureItemNavigation = function () {
var aPaletteColorsDomRefs = [],
aRecentColorsDomRefs = [];
if (!this._oPaletteColorItemNavigation) {
this._oPaletteColorItemNavigation = new ItemNavigationHomeEnd(this);
this._oPaletteColorItemNavigation.setColumns(SWATCHES_PER_ROW);
this._oPaletteColorItemNavigation.setCycling(false);
this.addDelegate(this._oPaletteColorItemNavigation);
this._oPaletteColorItemNavigation.attachEvent(ItemNavigation.Events.BorderReached, this._onSwatchContainerBorderReached, this);
this._oPaletteColorItemNavigation.setDisabledModifiers({
sapnext: ["alt", "meta"],
sapprevious: ["alt", "meta"],
saphome : ["alt", "meta"],
sapend : ["meta"]
});
}
if (!this._oRecentColorItemNavigation) {
this._oRecentColorItemNavigation = new ItemNavigationHomeEnd(this);
this._oRecentColorItemNavigation.setColumns(SWATCHES_PER_ROW);
this._oRecentColorItemNavigation.setCycling(false);
this.addDelegate(this._oRecentColorItemNavigation);
this._oRecentColorItemNavigation.attachEvent(ItemNavigation.Events.BorderReached, this._onSwatchContainerBorderReached, this);
this._oRecentColorItemNavigation.setDisabledModifiers({
sapnext: ["alt", "meta"],
sapprevious: ["alt", "meta"],
saphome : ["alt", "meta"],
sapend : ["meta"]
});
}
// all currently available swatches
aPaletteColorsDomRefs = aPaletteColorsDomRefs.concat(this._getAllPaletteColorSwatches());
aRecentColorsDomRefs = aRecentColorsDomRefs.concat(this._getAllRecentColorSwatches());
aRecentColorsDomRefs = aRecentColorsDomRefs.slice(0, this._getRecentColors().length);
this._oPaletteColorItemNavigation.setRootDomRef(this.getDomRef("swatchCont-paletteColor"));
this._oPaletteColorItemNavigation.setItemDomRefs(aPaletteColorsDomRefs);
this._oRecentColorItemNavigation.setRootDomRef(this.getDomRef("swatchCont-recentColors"));
this._oRecentColorItemNavigation.setItemDomRefs(aRecentColorsDomRefs);
};
/**
* Handles navigation from within the swatch container to the outside where the end-user navigates through the
* swatch container and reaches its border (<code>ItemNavigationHomeEnd.BorderReachedDirectionBackward</code> or
* <code>ItemNavigationHomeEnd.BorderReachedDirectionForward</code>).
*
* The buttons Default Color and More colors are handled as color palette items, these are also part of the
* arrow key navigation.
*
* If the user is on the last swatch item and presses [Down] or [Right] keyboard key
* (<code>ItemNavigationHomeEnd.BorderReachedDirectionForward</code>), the focus should go into one of the
* following elements (in the provided order):
* <ol>
* <li>More Colors button if such is available. If not, the focus should go to</li>
* <li>Default color button if such is available. If not, the focus should go to</li>
* <li>The first swatch item in the swatch container (there is always such)</li>
* </ol>
*
* If the user is on the first swatch item inside the swatch container and presses [Up] or [Down] keyboard keys
* (<code>ItemNavigationHomeEnd.BorderReachedDirectionBackward</code>) the focus should go into one of the
* following elements (in the provided order):
* <ol>
* <li>Default color button if such is available. If not, the focus should go to</li>
* <li>More Colors button if such is available. If not, the focus should go to</li>
* <li>The first swatch item in the last row of the swatch container (there is always such)</li>
* </ol>
*
* @param {jQuery.Event} oEvent the keyboard event
* @return {Element|sap.m.Button} the element that the focus has been moved on.
* @private
*/
ColorPalette.prototype._onSwatchContainerBorderReached = function(oEvent) {
var vNextElement,
aSwatches,
bHomeOrEnd = ["saphome","sapend"].indexOf(oEvent.getParameter("event").type) > -1,
bIsRecentColorSwatch = this._getAllRecentColorSwatches()[0] ? this._getElementInfo(oEvent.mParameters.event.target).bIsRecentColorSwatch : false;
if (oEvent.getParameter(ItemNavigationHomeEnd.BorderReachedDirection) === ItemNavigationHomeEnd.BorderReachedDirectionForward) {
if (this._getShowMoreColorsButton() && !bIsRecentColorSwatch) {
vNextElement = this._getMoreColorsButton();
} else if (!bHomeOrEnd && this._bShowRecentColorsSection && !bIsRecentColorSwatch && this._getRecentColors().length > 0) {
vNextElement = this._getAllRecentColorSwatches()[0];
} else if (!bHomeOrEnd && this._getShowDefaultColorButton()) {// Default Color, but excluding "home" and "end"
vNextElement = this._getDefaultColorButton();
} else if (!bHomeOrEnd) { // swatch, but not due to "home" and "end" keys
vNextElement = this._getAllPaletteColorSwatches()[0];
}
} else { // Backward
if (this._getShowDefaultColorButton() && !bIsRecentColorSwatch) {
vNextElement = this._getDefaultColorButton();
} else if (!bHomeOrEnd && this._bShowRecentColorsSection && !bIsRecentColorSwatch && this._getRecentColors().length > 0) {
vNextElement = this._getAllRecentColorSwatches()[0];
} else if (!bHomeOrEnd && this._getShowMoreColorsButton()) {// More Colors, but excluding "home" and "end"
vNextElement = this._getMoreColorsButton();
} else if (!bHomeOrEnd && !this._getShowDefaultColorButton()) { // swatch, but not due to "home" and "end" keys
aSwatches = this._getAllPaletteColorSwatches();
vNextElement = aSwatches[aSwatches.length - 1];
} else if (!bHomeOrEnd) { // swatch, but not due to "home" and "end" keys
aSwatches = this._getAllPaletteColorSwatches();
vNextElement = aSwatches[this._oPaletteColorItemNavigation._getIndexOfTheFirstItemInLastRow()];
}
}
if (vNextElement) {
vNextElement.focus();
}
return vNextElement;
};
/**
* Handles forward navigation when the user is either on Default Color or More Colors buttons.
*
* If the user is on Default Color, focus should go to the first item in the swatch container.
* If the user is on More Colors, focus should go on the Default Color button if such exists, otherwise on the
* the first item in the swatch container.
*
* @param {jQuery.Event} oEvent the keyboard event
*/
ColorPalette.prototype.onsapnext = function (oEvent) {
var vNextElement,
oElementInfo = this._getElementInfo(oEvent.target);
if (!(oElementInfo.bIsDefaultColorButton || oElementInfo.bIsMoreColorsButton)) {
return;
}
oEvent.preventDefault();
oEvent.stopImmediatePropagation(true); //also prevents ItemNavigation handler
if (oElementInfo.bIsDefaultColorButton) {
vNextElement = this._getAllPaletteColorSwatches()[0];
} else if (this._getRecentColors().length > 0 && !oElementInfo.bIsRecentColorSwatch && this._bShowRecentColorsSection){
vNextElement = this._getAllRecentColorSwatches()[0];
} else {
vNextElement = this._getShowDefaultColorButton() ? this._getDefaultColorButton() : this._getAllPaletteColorSwatches()[0];
}
vNextElement.focus();
};
/**
* Handles backward navigation when user is either on Default Color or More Colors buttons.
*
* If the user is on Default Color, focus should go to the More Colors button if such exists, otherwise,
* on the first item in the last row of the swatch container.
* If user is on More Colors, focus should go on:
* - the first item in the last row of the swatch container if <code>sapprevious</code> is fired due to
* [Up] key, otherwise
* - the last item in the swatch container (the possible keycodes is: [Left] (in ltr) or [Right] (rtl).
*
* @param {jQuery.Event} oEvent the keyboard event
*/
ColorPalette.prototype.onsapprevious = function (oEvent) {
var vNextElement,
oFocusInfo = this._getElementInfo(oEvent.target),
aAllSwatches;
if (!(oFocusInfo.bIsDefaultColorButton || oFocusInfo.bIsMoreColorsButton || oEvent.target === this._getAllRecentColorSwatches()[0])) {
return;
}
oEvent.preventDefault();
oEvent.stopImmediatePropagation(true);//also prevents ItemNavigation handler
aAllSwatches = this._getAllPaletteColorSwatches();
if (oFocusInfo.bIsMoreColorsButton || (!oFocusInfo.bIsMoreColorsButton && this.bIsRecentColorSwatch)) {
vNextElement = oEvent.keyCode === KeyCodes.ARROW_UP ?
aAllSwatches[this._oPaletteColorItemNavigation._getIndexOfTheFirstItemInLastRow()] : aAllSwatches[aAllSwatches.length - 1];
} else if (oFocusInfo.bIsRecentColorSwatch && !this._bShowMoreColorsButton && !this._bShowDefaultColorButton) {
aAllSwatches = this._getAllPaletteColorSwatches();
vNextElement = aAllSwatches[this._oPaletteColorItemNavigation._getIndexOfTheFirstItemInLastRow()];
} else if (this._getRecentColors().length > 0 && !oFocusInfo.bIsRecentColorSwatch && this._bShowRecentColorsSection){
vNextElement = this._getAllRecentColorSwatches()[0];
} else if (this._getShowMoreColorsButton()){
vNextElement = this._getMoreColorsButton();
} else {
vNextElement = aAllSwatches[this._oPaletteColorItemNavigation._getIndexOfTheFirstItemInLastRow()];
}
vNextElement.focus();
};
/**
* Handles backward navigation when the user is either on Default Color or More Colors buttons.
*
* If the user is on Default Color, focus shouldn't chage.
* If the user is on More Colors, focus should go on the Default Color button if such exists,
* otherwise the focus shouldn't change.
*
* @param {jQuery.Event} oEvent the keyboard event
*/
ColorPalette.prototype.onsaphome = function(oEvent) {
// Home and End keys on ColorPalette buttons should do nothing. If event occurs on the swatch, see ItemNavigationHomeEnd).
var oElementInfo = this._getElementInfo(oEvent.target);
if (!oElementInfo.bIsMoreColorsButton) {
return;
}
if (this._getShowDefaultColorButton()) {
this._getDefaultColorButton().focus();
}
oEvent.preventDefault();
oEvent.stopImmediatePropagation(true); // does not allow the ItemNavigationHomeEnd delegate to receive it
};
/**
* Handles forward navigation when the user is either on Default Color or More Colors buttons.
*
* If the user is on More Colors, focus shouldn't chage.
* If the user is on Default Color, focus should go on the More Colors button if such exists,
* otherwise the focus shouldn't change.
*
* @param {jQuery.Event} oEvent the keyboard event
*/
ColorPalette.prototype.onsapend = function(oEvent) {
var oElementInfo = this._getElementInfo(oEvent.target);
if (!oElementInfo.bIsDefaultColorButton) {
return;
}
if (this._getShowMoreColorsButton()) {
this._getMoreColorsButton().focus();
}
oEvent.preventDefault();
oEvent.stopImmediatePropagation(true); // does not allow the ItemNavigationHomeEnd delegate to receive it
};
// DOM related private helpers
/**
* Returns all palette swatches/squares
* @return {Element[]} returns all swatch container items in an array of DOM elements.
* @private
*/
ColorPalette.prototype._getAllPaletteColorSwatches = function () {
return this.$().find("." + CSS_CLASS_SWATCH).get().slice(0, this.getColors().length);
};
/**
* Returns all swatches/squares in recent color section
* @return {Element[]} returns all swatch container items in an array of DOM elements.
* @private
*/
ColorPalette.prototype._getAllRecentColorSwatches = function () {
return this.$().find("." + CSS_CLASS_SWATCH).get().slice(this.getColors().length);
};
/**
* Analyzes if given DOM element is one of the <code>ColorPalette</code> artifacts (Default Color, More Colors,
* swatch color).
* @param {Element} oElement DOM Element
* @return {{bIsDefaultColorButton: *, bIsMoreColorsButton: boolean|*, bIsASwatch: boolean|*}} result
* @private
*/
ColorPalette.prototype._getElementInfo = function (oElement) {
var bIsDefaultColorButton = this._getShowDefaultColorButton() && containsOrEquals(oElement,
this._getDefaultColorButton().getDomRef()),
bIsMoreColorsButton = !bIsDefaultColorButton && this._getShowMoreColorsButton() && containsOrEquals(oElement,
this._getMoreColorsButton().getDomRef()),
bIsRecentColorSwatch = this._getAllRecentColorSwatches().indexOf(oElement) > -1,
bIsASwatch = this._getAllPaletteColorSwatches().indexOf(oElement) > -1;
return {
bIsDefaultColorButton: bIsDefaultColorButton,
bIsMoreColorsButton: bIsMoreColorsButton,
bIsASwatch: bIsASwatch,
bIsRecentColorSwatch: bIsRecentColorSwatch
};
};
/**
* Extension of <code>sap.ui.core.delegate.ItemNavigation</code> to support custom keyboard handling for:
* - [Home] (overrides <code>onsaphome</code>,
* - [End] (overrides <code>onsapend</code>),
* - [Up] (overrides <code>onsapprevious</code>),
* - [Down] (overrides <code>onsapnext</code>).
*
* When using [Up] or [Down] keys, the focus moves up one row or down one row.
* If the next row has less items than the currently focused row, and the focus is above whitespace, the use of
* the [Down] arrow key moves the focus to the last item of the next row (-> also changes column). When using [Up] now,
* the focus moves one row up, onto the same column.
*
* <code>ItemNavigation</code>'s event <code>BorderReached</code> is enhanced. It used to be fired by [Right]
* (respectively [Left]) key on the last (respectively the first) item. Now, the same event is fired also when
* [Down] (respectively [Up]) key is used.
*
* If the [Home] key is pressed, it moves the focus according to the following order:
* - to the first item in the current row. If already on it
* - to the first item in the whole container. If already on it
* - fires a <code>BorderReached</code> event
*
* Respectively, if the [End] key is pressed, it moves the focus according to the following order:
* - to the last item in the current row. If already on it
* - to the last item in the whole container. If already on it
* - fires a <code>BorderReached</code> event
*
* @private
*/
var ItemNavigationHomeEnd = ItemNavigation.extend("sap.m.ItemNavigationHomeEnd", {
constructor: function () {
ItemNavigation.apply(this, arguments);
this.setHomeEndColumnMode(true);
// overrides fireEvent in order to enhance it with the parameter 'direction'
this.fireEvent = function(sName, oEventParams) {
var sDirection;
if (sName === ItemNavigation.Events.BorderReached) {
sDirection = ItemNavigationHomeEnd.BorderReachedDirectionBackward;
if (["sapnext", "sapend"].indexOf(oEventParams.event.type) > -1) { // last item
sDirection = ItemNavigationHomeEnd.BorderReachedDirectionForward;
}
oEventParams[ItemNavigationHomeEnd.BorderReachedDirection] = sDirection;
}
return ItemNavigation.prototype.fireEvent.apply(this, arguments);
};
}
});
// Custom Event Parameter to enhance ItemNavigation.BorderReached event with the direction of reach.
ItemNavigationHomeEnd.BorderReachedDirection = "direction";
ItemNavigationHomeEnd.BorderReachedDirectionForward = "BorderReachedDirectionForward";
ItemNavigationHomeEnd.BorderReachedDirectionBackward = "BorderReachedDirectionBackward";
/**
* Returns the number of columns defined.
* @return {*}
*/
ItemNavigationHomeEnd.prototype.getColumns = function() {
return this.iColumns;
};
/**
* Handles the backward navigation in case the [Up] key is used on the first swatch item.
* Otherwise, delegates the event to the <code>ItemNavigation</code>.
* @param {jQuery.Event} oEvent the keyboard event
*/
ItemNavigationHomeEnd.prototype.onsapprevious = function (oEvent) {
var bIsOnItem = containsOrEquals(this.getRootDomRef(), oEvent.target),
bArrowUpOnFirstItem = oEvent.keyCode === KeyCodes.ARROW_UP && this.getFocusedIndex() === 0;
if (!bIsOnItem) {
return;
}
if (!bArrowUpOnFirstItem) {
ItemNavigation.prototype.onsapprevious.apply(this, arguments);
return;
}
oEvent.preventDefault(); // browser's scrolling should be prevented
this.fireEvent(ItemNavigation.Events.BorderReached, {
index: 0,
event: oEvent
});
};
/**
* Handles the forward navigation when the [Down] key is used in the following cases:
* - on the last swatch item
* - on an upper row item when moving on the same column of the next row will hit an empty/whitespace swatch item.
* Otherwise delegates the event to the <code>ItemNavigation</code>.
*
* @param {jQuery.Event} oEvent the keyboard event
*/
ItemNavigationHomeEnd.prototype.onsapnext = function (oEvent) {
var bIsOnItem = containsOrEquals(this.getRootDomRef(), oEvent.target),
aItemDomRefs,
iCurrentIndex,
oItemInfo;
if (!bIsOnItem) {
return;
}
if (oEvent.keyCode !== KeyCodes.ARROW_DOWN) {
ItemNavigation.prototype.onsapnext.apply(this, arguments);
return;
}
iCurrentIndex = this.getFocusedIndex();
oItemInfo = this._getItemInfo(iCurrentIndex);
if (oItemInfo.bIsLastItem && oItemInfo.bIsInTheLastColumn) {
oEvent.preventDefault(); //browser's scrolling should be prevented
this.fireEvent(ItemNavigation.Events.BorderReached, {// Arrow down on last item should fire the event "BorderReached"
index: iCurrentIndex,
event: oEvent
});
return;
}
// Whitespace handler
if (oItemInfo.bNextRowExists && !oItemInfo.bItemSameColumnNextRowExists) {
oEvent.preventDefault(); //browser's scrolling should be prevented
aItemDomRefs = this.getItemDomRefs();
aItemDomRefs[aItemDomRefs.length - 1].focus();
return;
}
ItemNavigation.prototype.onsapnext.apply(this, arguments);
};
ItemNavigationHomeEnd.prototype.onsaphome = function(oEvent) {
var bIsOnItem = containsOrEquals(this.getRootDomRef(), oEvent.target),
oItemInfo;
if (!bIsOnItem) {
return;
}
oItemInfo = this._getItemInfo(this.getFocusedIndex());
if (!oItemInfo.bIsInTheFirstColumn) {
// delegate to the parent, which should move the focus to the first item in the row
ItemNavigation.prototype.onsaphome.apply(this, arguments);
return;
}
oEvent.preventDefault(); // browser's scrolling should be prevented
if (oItemInfo.bIsFirstItem) {
this.fireEvent(ItemNavigation.Events.BorderReached, {
index: 0,
event: oEvent
});
} else { //first item in the row, move the focus to the first item in the whole container
this.getItemDomRefs()[0].focus();
}
};
ItemNavigationHomeEnd.prototype.onsapend = function(oEvent) {
var bIsOnItem = containsOrEquals(this.getRootDomRef(), oEvent.target),
oItemInfo;
if (!bIsOnItem) {
return;
}
oItemInfo = this._getItemInfo(this.getFocusedIndex());
if (!(oItemInfo.bIsLastItem || oItemInfo.bIsInTheLastColumn)) {
// delegate to the parent, which should move the focus to the last item in the row
ItemNavigation.prototype.onsapend.apply(this, arguments);
return;
}
oEvent.preventDefault(); // browser's scrolling should be prevented
if (oItemInfo.bIsLastItem) {
this.fireEvent(ItemNavigation.Events.BorderReached, {
index: this.getItemDomRefs().length - 1,
event: oEvent
});
} else { // last item in the row, move the focus to the last item in the whole container
this.getItemDomRefs()[this.getItemDomRefs().length - 1].focus();
}
};
/**
* Analyzes the given item and produces information about its position.
* @param {number} iIndex the item given by its position
* @return {{bIsLastItem: boolean, bIsInTheLastColumn: boolean, bNextRowExists: boolean|*, bItemSameColumnNextRowExists: boolean|*}}
* @private
*/
ItemNavigationHomeEnd.prototype._getItemInfo = function(iIndex) {
var iItemsCount = this.getItemDomRefs().length,
bItemIsLast = iIndex === (iItemsCount - 1),
iLastVisibleColumn = iItemsCount > this.getColumns() ? this.getColumns() : iItemsCount,
bItemIsInTheFirstColumn = iIndex % this.getColumns() === 0,
bItemIsInTheLastColumn = (iIndex + 1) % iLastVisibleColumn === 0,
iCurrentRow = Math.floor(iIndex / this.getColumns()) + 1, //1 based
bNextRowExists,
bItemSameColumnNextRowExists;
bNextRowExists = iCurrentRow * this.getColumns() < iItemsCount;
bItemSameColumnNextRowExists = bNextRowExists && (iIndex + this.getColumns()) < iItemsCount;
return {
bIsFirstItem: iIndex === 0,
bIsLastItem: bItemIsLast,
bIsInTheLastColumn: bItemIsInTheLastColumn,
bIsInTheFirstColumn: bItemIsInTheFirstColumn,
bNextRowExists: bNextRowExists,
bItemSameColumnNextRowExists: bItemSameColumnNextRowExists
};
};
/**
* Calculates the index of the first item in the last row.
* @return {int} the index(zero based) of the first/last item in the row.
* @private
*/
ItemNavigationHomeEnd.prototype._getIndexOfTheFirstItemInLastRow = function () {
return Math.floor((this.getItemDomRefs().length - 1) / this.getColumns()) * this.getColumns();
};
/**
* @private
*/
ColorPalette.prototype._ItemNavigation = ItemNavigationHomeEnd;
/**
* Private Color map helper
* @private
*/
ColorPalette.prototype._ColorsHelper = {
RGB_TO_NAMED_COLORS_MAP: {
"#FFB200": "gold",
"#FF8C00": "darkorange",
"#CD5C5C": "indianred",
"#8B008B": "darkmagenta",
"#6495ED": "cornflowerblue",
"#00BFFF": "deepskyblue",
"#008B8B": "darkcyan",
"#6B8E23": "olivedrab",
"#2F4F4F": "darkslategray",
"#F0FFFF": "azure",
"#FFFFFF": "white",
"#D3D3D3": "lightgray",
"#A9A9A9": "darkgray",
"#696969": "dimgray",
"#000000": "black"
},
NAME_COLORS_TO_RGB_MAP: {
"gold": "#FFB200",
"darkorange": "#FF8C00",
"indianred": "#CD5C5C",
"darkmagenta": "#8B008B",
"cornflowerblue": "#6495ED",
"deepskyblue": "#00BFFF",
"darkcyan": "#008B8B",
"olivedrab": "#6B8E23",
"darkslategray": "#2F4F4F",
"azure": "#F0FFFF",
"white": "#FFFFFF",
"lightgray": "#D3D3D3",
"darkgray": "#A9A9A9",
"dimgray": "#696969",
"black": "#000000"
},
/**
* Returns a named color for given color. For example - "gold" for input "#FFB200".
* @param {string} sColor the given color
* @return {string|undefined} The named color, if such can really corresponds to the input color, or undefined otherwise.
*/
getNamedColor: function (sColor) {
var sHexColor = "";
if (!sColor || sColor.toLowerCase().indexOf("hsl") !== -1) {
return undefined;
}
// named color
if (sColor.indexOf("#") === -1) {
return this.NAME_COLORS_TO_RGB_MAP[sColor.toLowerCase()] ? sColor.toLowerCase() : undefined;
}
//HEX value
if (sColor.length === 4) {
sHexColor = ["#", sColor[1], sColor[1], sColor[2], sColor[2], sColor[3], sColor[3]].join("");
} else {
sHexColor = sColor;
}
sHexColor = sHexColor.toUpperCase();
return this.RGB_TO_NAMED_COLORS_MAP[sHexColor];
}
};
return ColorPalette;
}
);