UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

463 lines (461 loc) • 26.1 kB
define(["require", "exports", "tslib", "react", "../../Utilities", "./SwatchColorPicker.Props", "../../ContextualMenu", "../../utilities/color/colors", "../../utilities/grid/Grid", "../../Button", "../../Callout", "../../FocusZone", "./SwatchColorPicker.scss"], function (require, exports, tslib_1, React, Utilities_1, SwatchColorPicker_Props_1, ContextualMenu_1, colors_1, Grid_1, Button_1, Callout_1, FocusZone_1, stylesImport) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var styles = stylesImport; var SwatchColorPickerOption = function (props) { var item = props.item, id = props.id, posInSet = props.posInSet, setSize = props.setSize, className = props.className, role = props.role, selectedIndex = props.selectedIndex, disabled = props.disabled, cellShape = props.cellShape, onClick = props.onClick, onHover = props.onHover, onFocus = props.onFocus; return (React.createElement(Button_1.CommandButton, tslib_1.__assign({}, item.menuItemButtonProps, { id: id + '-item' + item.index, "data-index": item.index, "data-is-focusable": true, "aria-posinset": posInSet && posInSet, "aria-setsize": setSize && setSize, disabled: disabled, className: Utilities_1.css(className, (_a = {}, _a['is-selected ' + styles.cellIsSelected] = (selectedIndex && selectedIndex === item.index), _a['is-disabled ' + styles.disabled] = disabled, _a)), onClick: _onClick, onMouseEnter: _onMouseEnter, onMouseLeave: _onMouseLeave, onFocus: _onFocus, role: role, "aria-selected": selectedIndex && (selectedIndex === item.index).toString(), ariaLabel: item.label && item.label, title: item.label && item.label }), _onRenderOption())); function _onClick() { if (onClick && !disabled) { onClick(item); } } function _onMouseEnter() { if (onHover && !disabled) { onHover(item.id, item.color); } } function _onMouseLeave() { if (onHover && !disabled) { onHover(); } } function _onFocus() { if (onFocus && !disabled) { onFocus(item.id, item.color); } } /** * Render the core of an cell or menu item * @returns {JSX.Element} - Element representing the core of the item */ function _onRenderOption() { // Menu items just need their label text if (item.type !== SwatchColorPicker_Props_1.SwatchColorPickerItemType.Cell) { return React.createElement("span", { className: styles.menuItem }, item.label); } // Build an SVG for the cell with the given shape and color properties return (React.createElement("svg", { className: Utilities_1.css(styles.svg, cellShape, cellShape === 'circle' ? styles.circle : ''), viewBox: '0 0 20 20', fill: colors_1.getColorFromString(item.color).str }, cellShape === 'circle' ? React.createElement("circle", { cx: '50%', cy: '50%', r: '50%' }) : React.createElement("rect", { width: '100%', height: '100%' }))); } var _a; }; var SwatchColorPicker = (function (_super) { tslib_1.__extends(SwatchColorPicker, _super); function SwatchColorPicker(props) { var _this = _super.call(this, props) || this; _this._id = props.id || Utilities_1.getId('swatchColorPicker'); var selectedIndex; if (props.selectedId) { selectedIndex = _this._getSelectedIndex(props.swatchColorPickerItems, props.selectedId); } _this.state = { selectedIndex: selectedIndex, expanded: false }; return _this; } SwatchColorPicker.prototype.componentWillReceiveProps = function (newProps) { var newSelectedIndex; if (newProps.selectedId) { newSelectedIndex = this._getSelectedIndex(newProps.swatchColorPickerItems, newProps.selectedId); } if (newSelectedIndex !== undefined && newSelectedIndex !== this.state.selectedIndex) { this.setState({ selectedIndex: newSelectedIndex }); } }; SwatchColorPicker.prototype.render = function () { var expanded = this.state.expanded; var _a = this.props, menuButtonProps = _a.menuButtonProps, disabled = _a.disabled, swatchColorPickerItems = _a.swatchColorPickerItems, columnCount = _a.columnCount; // If we got button props, put the swatch color picker behind a button, otherwise // render all of the items var colorToSet = this._getSelectedColorToSet(); var renderElement = menuButtonProps !== undefined ? React.createElement(SwatchColorPickerMenuButton, tslib_1.__assign({}, this.props, { color: colorToSet, onClick: this._onButtonClick, expanded: !!this.state.expanded, onRenderContainer: this._onRenderContainer, menuIconProps: menuButtonProps.menuIconProps ? menuButtonProps.menuIconProps : { iconName: 'chevronDown' } })) : React.createElement(SwatchColorPickerBody, tslib_1.__assign({}, this.props, { onRenderItems: this._onRenderItems })); return (React.createElement("div", { ref: this._resolveRef('_buttonWrapper'), className: Utilities_1.css('ms-swatchColorPickerWrapper', styles.wrapper) }, renderElement)); }; /** * Get the selected item's index * @param items - The items to search * @param selectedId - The selected item's id to find * @returns {number} - The index of the selected item's id, -1 if there was no match */ SwatchColorPicker.prototype._getSelectedIndex = function (items, selectedId) { var selectedIndex = Utilities_1.findIndex(items, (function (item) { return (item.id === selectedId); })); return selectedIndex >= 0 ? selectedIndex : undefined; }; /** * Gets the color for the selected index * @returns {string} - The color for the selected index, * or undefined if: we are not updating the button icon with color, * there is not a valid selected index, or if we do not have a valid color */ SwatchColorPicker.prototype._getSelectedColorToSet = function () { var selectedIndex = this.state.selectedIndex; var _a = this.props, swatchColorPickerItems = _a.swatchColorPickerItems, setSelectedColorForIcon = _a.setSelectedColorForIcon; // Do we need to update the button with the selected // item's color? If so, attempt to grab the color if (setSelectedColorForIcon && selectedIndex !== undefined) { var swatchColorPickerItem = swatchColorPickerItems[selectedIndex]; if (swatchColorPickerItem.type === SwatchColorPicker_Props_1.SwatchColorPickerItemType.Cell) { return swatchColorPickerItem.color; } } return undefined; }; /** * Render all the items * @param items - The swatch color picker items * @returns {JSX.Element[]} - An array of all the items in the swatch color picker */ SwatchColorPicker.prototype._onRenderItems = function (items) { // This holds all of the element for the items var elements = []; // The number of cells that were processed in the previous iteration of loop. // This will be used to increase the index to the item after the last processed // item var numOfItemsInChunk = -1; // If we have cell items and we are in a menu, get all the // first executable items per chunk. Note, in this // context each menuItem is in its own "chunk", only grouped // cells are processed as a chunk. This helps with being able // to determine the correct aria-posinset and aria-setsize values var firstExecutableItemsPerChunk = (this.props.menuButtonProps ? this._getFirstExecutableItemsPerChunk() : undefined); // Did we find any executable items? (e.g. should be calculate the set information) var shouldGetSetInfo = (firstExecutableItemsPerChunk && firstExecutableItemsPerChunk.length > 0); var setSize = shouldGetSetInfo ? firstExecutableItemsPerChunk.length : undefined; // If any menuItem has an icon, all menu items need to be positined correctly so they align var shouldAccountForIcon = (firstExecutableItemsPerChunk && Utilities_1.findIndex(firstExecutableItemsPerChunk, function (executableItem) { return (executableItem.type === SwatchColorPicker_Props_1.SwatchColorPickerItemType.MenuItem && executableItem.menuItemButtonProps); })); // Loop across the items processing them depending on their item type var index = 0; while (index < items.length) { var item = items[index]; var posInSet = shouldGetSetInfo ? this._getPositionInSet(firstExecutableItemsPerChunk, item.id) : undefined; switch (item.type) { case SwatchColorPicker_Props_1.SwatchColorPickerItemType.Divider: elements.push(this._renderSeparator(item)); break; case SwatchColorPicker_Props_1.SwatchColorPickerItemType.Header: elements.push(this._renderHeader(item)); break; case SwatchColorPicker_Props_1.SwatchColorPickerItemType.Cell: // build all the cells in the chunk this cell // exists within (this will process all of the // consecutive cells until the next non-cell type // is incountered (or if we reach the end of the items)) var chunkItems = this._getNextChunkOfCellItems(items.slice(index)); // Update the number of items in chunk numOfItemsInChunk = chunkItems.length > 0 ? chunkItems.length : -1; // Add the result to the array elements.push(this._renderNextChunkOfCellItems(chunkItems, posInSet, setSize)); break; default: // Does at least one of the menu items have an icon? if (shouldAccountForIcon) { // Make sure the width styling is the same by applying a consistent className if (item.menuItemButtonProps) { item.menuItemButtonProps = tslib_1.__assign({}, item.menuItemButtonProps, { iconProps: tslib_1.__assign({}, item.menuItemButtonProps.iconProps, { className: styles.icon }) }); } else { // This menu item didn't have an icon so add a "spacer" icon to make the text // content align. This aligns with how ContextualMenu achieves this alignmet item.menuItemButtonProps = tslib_1.__assign({}, item.menuItemButtonProps, { iconProps: { iconName: 'customIcon', className: styles.icon } }); } } elements.push(this._renderOption(item, posInSet, setSize)); } // Increase the index by the number of items in the just processed chunk, // otherwise just increment the index index += numOfItemsInChunk > 0 ? numOfItemsInChunk : 1; numOfItemsInChunk = -1; } return elements; }; /** * Renders the next consecutive chunk of cells * @param items - The items to process * @param posInSet - Optional, the position in the set for this chunk * @param setSize - Optional, the size of the total set * @returns {JSX.Element} - The grid that represents the chunk */ SwatchColorPicker.prototype._renderNextChunkOfCellItems = function (items, posInSet, setSize) { return (React.createElement(Grid_1.Grid, { key: this._id + items[0].id + '-grid', items: items, columnCount: this.props.columnCount, onRenderItem: this._renderOption, positionInSet: posInSet, setSize: setSize })); }; /** * Get the position in set for the given id * @param firstExecutableItemsPerChunk - The list of fist executable items per chunk * @param itemId - The id of item to find the index of * @returns {number} - The position in the set */ SwatchColorPicker.prototype._getPositionInSet = function (firstExecutableItemsPerChunk, itemId) { // Find the index of the given id in the list of first executable items per chunk var index = Utilities_1.findIndex(firstExecutableItemsPerChunk, function (executableItem) { return (executableItem.id === itemId); }); return index < 0 ? undefined : index + 1; }; /** * Gets the next chunk of consecutive cells starting a index zero. * Note, index zero should be of type cell * @param items - The list of items to process where index zero is the start of the chunk * @returns {ISwatchColorPickerItemProps[]} - the array of consecutive cells starting at index zero * (and continuing to the first non-cell type or the end of the items) */ SwatchColorPicker.prototype._getNextChunkOfCellItems = function (items) { var nextIndextAfterChunk = Utilities_1.findIndex(items, (function (item) { return item.type !== SwatchColorPicker_Props_1.SwatchColorPickerItemType.Cell; })); // If we didn't find a non-cell item, we need to handle everything that is left if (nextIndextAfterChunk < 0) { return items; } // If we get here we found our chunk boundry return items.slice(0 /* start */, nextIndextAfterChunk); }; /** * Get only the executable items (cells and menuItems) * @returns {ISwatchColorPickerItemProps[]} - an array of the executable items */ SwatchColorPicker.prototype._getFirstExecutableItemsPerChunk = function () { // Make sure every item has an index, then filter // the results so that you only get the executable items, // finally filter those items down to just the items that are // either the start of a chunk or an menu item return (this.props.swatchColorPickerItems.map(function (item, index) { return tslib_1.__assign({}, item, { index: index }); }) .filter(function (item) { return (item.type === SwatchColorPicker_Props_1.SwatchColorPickerItemType.Cell || item.type === SwatchColorPicker_Props_1.SwatchColorPickerItemType.MenuItem); }) .filter(function (item, filteredIndex, items) { return (filteredIndex === 0 || item.type === SwatchColorPicker_Props_1.SwatchColorPickerItemType.MenuItem || (item.index - items[filteredIndex - 1].index !== 1)); })); }; /** * Render the separator * @param item - The divider item to get the data to render from * @returns {JSX.Element} - Element that represents the separator */ SwatchColorPicker.prototype._renderSeparator = function (item) { return this._renderHeaderOrDivider(item, 'separator', styles.divider); }; /** * Render the header * @param item - The header item to get the data to render from * @returns {JSX.Element} - Element that represents the header */ SwatchColorPicker.prototype._renderHeader = function (item) { return this._renderHeaderOrDivider(item, 'heading', styles.header); }; /** * Handle the rendering of the header/divider * @param item - The item to get the data to render from * @param role - The role of the element * @param className - the className to use * @returns {JSX.Element} - Element that represents the header */ SwatchColorPicker.prototype._renderHeaderOrDivider = function (item, role, className) { return React.createElement("div", { role: role, key: item.id, className: className }, item.label && item.label); }; /** * Render a cell or menu item * @param item - The item to render * @param posInSet - Optional, the position in the set of the item * @param setSize - Optional, the total set size this item is in * @returns {JSX.Element} - Element representing the item */ SwatchColorPicker.prototype._renderOption = function (item, posInSet, setSize) { var id = this._id; var isCell = item.type === SwatchColorPicker_Props_1.SwatchColorPickerItemType.Cell; var optionProps; if (isCell) { optionProps = { className: styles.cell, onClick: this._onCellClick, onHover: this.props.onCellHovered, onFocus: this.props.onCellFocused, role: 'gridcell', selectedIndex: this.state.selectedIndex }; } else { optionProps = { posInSet: posInSet && posInSet, setSize: setSize && setSize, className: styles.item, onClick: this._onMenuItemClick, onFocus: this._clearFocusColorOnMenuItem, role: this.props.menuButtonProps ? 'menuitem' : 'button' }; } return (React.createElement(SwatchColorPickerOption, tslib_1.__assign({ item: item, id: this._id, key: id + item.id, disabled: this.props.disabled || item.disabled, cellShape: this.props.cellShape }, optionProps))); }; /** * Handle the click on a cell * @param item - The cell that the click was fired against */ SwatchColorPicker.prototype._onCellClick = function (item) { if (this.props.disabled || item.disabled) { return; } var index = item.index; // If we have a valid index and it is not already // selected, select it if (index >= 0 && index !== this.state.selectedIndex) { if (this.props.onColorChanged) { this.props.onColorChanged(item.id, item.color); } this.setState({ selectedIndex: index, expanded: false }); } else if (index === this.state.selectedIndex) { // The index that got the click was already selected, // clear the selection this._clearColors([this.props.onColorChanged, this.props.onCellHovered, this.props.onCellFocused]); this.setState({ selectedIndex: undefined, expanded: false }); } }; /** * Handle the click on a menu item * @param item - The menu item that the click was fired against */ SwatchColorPicker.prototype._onMenuItemClick = function (item) { if (this.props.disabled || item.disabled) { return; } if (this.props.onMenuItemClick) { this.props.onMenuItemClick(item); } // Make sure to clear any hover/focus colors this._clearColors([this.props.onCellHovered, this.props.onCellFocused]); this.setState({ expanded: false }); }; /** * Clear the colors by calling the given callbacks * @param callbacks - The callbacks to handle the clear operation */ SwatchColorPicker.prototype._clearColors = function (callbacks) { callbacks.forEach(function (callback) { if (callback) { callback(); } }); }; /** * Clear the focus color * @param id - The id of the item * @param color - The color for the item */ SwatchColorPicker.prototype._clearFocusColorOnMenuItem = function (id, color) { this._clearColors([this.props.onCellFocused]); }; /** * onClick Handler for the button */ SwatchColorPicker.prototype._onButtonClick = function () { if (this.props.disabled) { return; } if (this.state.expanded) { this._onDismiss(); } this.setState({ expanded: this.props.disabled ? false : !this.state.expanded }); }; /** * Render the menu (callout) for when the swatch color picker * is behind a button and is expanded */ SwatchColorPicker.prototype._onRenderContainer = function () { return (React.createElement(Callout_1.Callout, { isBeakVisible: false, gapSpace: 0, doNotLayer: false, role: 'menu', directionalHint: ContextualMenu_1.DirectionalHint.bottomLeftEdge, className: Utilities_1.css('ms-swatchColorPickerMenu', styles.swatchColorPickerContainer), targetElement: this._buttonWrapper, onDismiss: this._onDismiss, setInitialFocus: true }, React.createElement(SwatchColorPickerBody, tslib_1.__assign({}, this.props, { onRenderItems: this._onRenderItems })))); }; /** * Handle dismissing the menu */ SwatchColorPicker.prototype._onDismiss = function () { this._clearColors([this.props.onCellHovered, this.props.onCellFocused]); this.setState({ expanded: false }); }; return SwatchColorPicker; }(Utilities_1.BaseComponent)); SwatchColorPicker.defaultProps = { cellShape: 'circle', updateButtonIconWithColor: false, disabled: false }; tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_onRenderItems", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_renderOption", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_onCellClick", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_onMenuItemClick", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_clearFocusColorOnMenuItem", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_onButtonClick", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_onRenderContainer", null); tslib_1.__decorate([ Utilities_1.autobind ], SwatchColorPicker.prototype, "_onDismiss", null); exports.SwatchColorPicker = SwatchColorPicker; var SwatchColorPickerMenuButton = (function (_super) { tslib_1.__extends(SwatchColorPickerMenuButton, _super); function SwatchColorPickerMenuButton() { return _super !== null && _super.apply(this, arguments) || this; } SwatchColorPickerMenuButton.prototype.render = function () { var _a = this.props, color = _a.color, expanded = _a.expanded, disabled = _a.disabled, onClick = _a.onClick, onRenderContainer = _a.onRenderContainer, menuIconProps = _a.menuIconProps, menuButtonProps = _a.menuButtonProps; return (React.createElement("div", null, React.createElement(Button_1.DefaultButton, tslib_1.__assign({}, menuButtonProps, { style: { color: color && color }, className: Utilities_1.css('ms-swatchColorPickerButton', { 'is-expanded': expanded }), onClick: onClick, "aria-haspopup": true, "aria-expanded": !disabled && expanded, disabled: disabled, menuIconProps: menuIconProps ? menuIconProps : { iconName: 'chevronDown' } })), (!disabled && expanded) && onRenderContainer())); }; return SwatchColorPickerMenuButton; }(Utilities_1.BaseComponent)); SwatchColorPickerMenuButton.defaultProps = { expanded: false, disabled: false }; var SwatchColorPickerBody = (function (_super) { tslib_1.__extends(SwatchColorPickerBody, _super); function SwatchColorPickerBody() { return _super !== null && _super.apply(this, arguments) || this; } SwatchColorPickerBody.prototype.render = function () { var _a = this.props, swatchColorPickerItems = _a.swatchColorPickerItems, columnCount = _a.columnCount, onRenderItems = _a.onRenderItems; return (React.createElement(FocusZone_1.FocusZone, { isCircularNavigation: true, className: Utilities_1.css('ms-swatchColorPickerBodyContainer', styles.swatchColorPickerContainer) }, onRenderItems(swatchColorPickerItems.map(function (item, index) { return tslib_1.__assign({}, item, { index: index }); })))); }; return SwatchColorPickerBody; }(Utilities_1.BaseComponent)); }); //# sourceMappingURL=SwatchColorPicker.js.map